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 + */ + int seek_preroll; + +#if !FF_API_DEBUG_MV + /** + * debug motion vectors + * - encoding: Set by user. + * - decoding: Set by user. + */ + int debug_mv; +#define FF_DEBUG_VIS_MV_P_FOR 0x00000001 //visualize forward predicted MVs of P frames +#define FF_DEBUG_VIS_MV_B_FOR 0x00000002 //visualize forward predicted MVs of B frames +#define FF_DEBUG_VIS_MV_B_BACK 0x00000004 //visualize backward predicted MVs of B frames +#endif + + /** + * custom intra quantization matrix + * - encoding: Set by user, can be NULL. + * - decoding: unused. + */ + uint16_t *chroma_intra_matrix; + + /** + * dump format separator. + * can be ", " or "\n " or anything else + * - encoding: Set by user. + * - decoding: Set by user. + */ + uint8_t *dump_separator; + + /** + * ',' separated list of allowed decoders. + * If NULL then all are allowed + * - encoding: unused + * - decoding: set by user + */ + char *codec_whitelist; + + /** + * Properties of the stream that gets decoded + * - encoding: unused + * - decoding: set by libavcodec + */ + unsigned properties; +#define FF_CODEC_PROPERTY_LOSSLESS 0x00000001 +#define FF_CODEC_PROPERTY_CLOSED_CAPTIONS 0x00000002 + + /** + * Additional data associated with the entire coded stream. + * + * - decoding: unused + * - encoding: may be set by libavcodec after avcodec_open2(). + */ + AVPacketSideData *coded_side_data; + int nb_coded_side_data; + + /** + * A reference to the AVHWFramesContext describing the input (for encoding) + * or output (decoding) frames. The reference is set by the caller and + * afterwards owned (and freed) by libavcodec - it should never be read by + * the caller after being set. + * + * - decoding: This field should be set by the caller from the get_format() + * callback. The previous reference (if any) will always be + * unreffed by libavcodec before the get_format() call. + * + * If the default get_buffer2() is used with a hwaccel pixel + * format, then this AVHWFramesContext will be used for + * allocating the frame buffers. + * + * - encoding: For hardware encoders configured to use a hwaccel pixel + * format, this field should be set by the caller to a reference + * to the AVHWFramesContext describing input frames. + * AVHWFramesContext.format must be equal to + * AVCodecContext.pix_fmt. + * + * This field should be set before avcodec_open2() is called. + */ + AVBufferRef *hw_frames_ctx; + + /** + * Control the form of AVSubtitle.rects[N]->ass + * - decoding: set by user + * - encoding: unused + */ + int sub_text_format; +#define FF_SUB_TEXT_FMT_ASS 0 +#if FF_API_ASS_TIMING +#define FF_SUB_TEXT_FMT_ASS_WITH_TIMINGS 1 +#endif + + /** + * Audio only. The amount of padding (in samples) appended by the encoder to + * the end of the audio. I.e. this number of decoded samples must be + * discarded by the caller from the end of the stream to get the original + * audio without any trailing padding. + * + * - decoding: unused + * - encoding: unused + */ + int trailing_padding; + + /** + * The number of pixels per image to maximally accept. + * + * - decoding: set by user + * - encoding: set by user + */ + int64_t max_pixels; + + /** + * A reference to the AVHWDeviceContext describing the device which will + * be used by a hardware encoder/decoder. The reference is set by the + * caller and afterwards owned (and freed) by libavcodec. + * + * This should be used if either the codec device does not require + * hardware frames or any that are used are to be allocated internally by + * libavcodec. If the user wishes to supply any of the frames used as + * encoder input or decoder output then hw_frames_ctx should be used + * instead. When hw_frames_ctx is set in get_format() for a decoder, this + * field will be ignored while decoding the associated stream segment, but + * may again be used on a following one after another get_format() call. + * + * For both encoders and decoders this field should be set before + * avcodec_open2() is called and must not be written to thereafter. + * + * Note that some decoders may require this field to be set initially in + * order to support hw_frames_ctx at all - in that case, all frames + * contexts used must be created on the same device. + */ + AVBufferRef *hw_device_ctx; + + /** + * Bit set of AV_HWACCEL_FLAG_* flags, which affect hardware accelerated + * decoding (if active). + * - encoding: unused + * - decoding: Set by user (either before avcodec_open2(), or in the + * AVCodecContext.get_format callback) + */ + int hwaccel_flags; + + /** + * Video decoding only. Certain video codecs support cropping, meaning that + * only a sub-rectangle of the decoded frame is intended for display. This + * option controls how cropping is handled by libavcodec. + * + * When set to 1 (the default), libavcodec will apply cropping internally. + * I.e. it will modify the output frame width/height fields and offset the + * data pointers (only by as much as possible while preserving alignment, or + * by the full amount if the AV_CODEC_FLAG_UNALIGNED flag is set) so that + * the frames output by the decoder refer only to the cropped area. The + * crop_* fields of the output frames will be zero. + * + * When set to 0, the width/height fields of the output frames will be set + * to the coded dimensions and the crop_* fields will describe the cropping + * rectangle. Applying the cropping is left to the caller. + * + * @warning When hardware acceleration with opaque output frames is used, + * libavcodec is unable to apply cropping from the top/left border. + * + * @note when this option is set to zero, the width/height fields of the + * AVCodecContext and output AVFrames have different meanings. The codec + * context fields store display dimensions (with the coded dimensions in + * coded_width/height), while the frame fields store the coded dimensions + * (with the display dimensions being determined by the crop_* fields). + */ + int apply_cropping; + + /* + * Video decoding only. Sets the number of extra hardware frames which + * the decoder will allocate for use by the caller. This must be set + * before avcodec_open2() is called. + * + * Some hardware decoders require all frames that they will use for + * output to be defined in advance before decoding starts. For such + * decoders, the hardware frame pool must therefore be of a fixed size. + * The extra frames set here are on top of any number that the decoder + * needs internally in order to operate normally (for example, frames + * used as reference pictures). + */ + int extra_hw_frames; +} AVCodecContext; + +#if FF_API_CODEC_GET_SET +/** + * Accessors for some AVCodecContext fields. These used to be provided for ABI + * compatibility, and do not need to be used anymore. + */ +attribute_deprecated +AVRational av_codec_get_pkt_timebase (const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_pkt_timebase (AVCodecContext *avctx, AVRational val); + +attribute_deprecated +const AVCodecDescriptor *av_codec_get_codec_descriptor(const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_codec_descriptor(AVCodecContext *avctx, const AVCodecDescriptor *desc); + +attribute_deprecated +unsigned av_codec_get_codec_properties(const AVCodecContext *avctx); + +#if FF_API_LOWRES +attribute_deprecated +int av_codec_get_lowres(const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_lowres(AVCodecContext *avctx, int val); +#endif + +attribute_deprecated +int av_codec_get_seek_preroll(const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_seek_preroll(AVCodecContext *avctx, int val); + +attribute_deprecated +uint16_t *av_codec_get_chroma_intra_matrix(const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_chroma_intra_matrix(AVCodecContext *avctx, uint16_t *val); +#endif + +/** + * AVProfile. + */ +typedef struct AVProfile { + int profile; + const char *name; ///< short name for the profile +} AVProfile; + +enum { + /** + * The codec supports this format via the hw_device_ctx interface. + * + * When selecting this format, AVCodecContext.hw_device_ctx should + * have been set to a device of the specified type before calling + * avcodec_open2(). + */ + AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX = 0x01, + /** + * The codec supports this format via the hw_frames_ctx interface. + * + * When selecting this format for a decoder, + * AVCodecContext.hw_frames_ctx should be set to a suitable frames + * context inside the get_format() callback. The frames context + * must have been created on a device of the specified type. + */ + AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX = 0x02, + /** + * The codec supports this format by some internal method. + * + * This format can be selected without any additional configuration - + * no device or frames context is required. + */ + AV_CODEC_HW_CONFIG_METHOD_INTERNAL = 0x04, + /** + * The codec supports this format by some ad-hoc method. + * + * Additional settings and/or function calls are required. See the + * codec-specific documentation for details. (Methods requiring + * this sort of configuration are deprecated and others should be + * used in preference.) + */ + AV_CODEC_HW_CONFIG_METHOD_AD_HOC = 0x08, +}; + +typedef struct AVCodecHWConfig { + /** + * A hardware pixel format which the codec can use. + */ + enum AVPixelFormat pix_fmt; + /** + * Bit set of AV_CODEC_HW_CONFIG_METHOD_* flags, describing the possible + * setup methods which can be used with this configuration. + */ + int methods; + /** + * The device type associated with the configuration. + * + * Must be set for AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX and + * AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, otherwise unused. + */ + enum AVHWDeviceType device_type; +} AVCodecHWConfig; + +typedef struct AVCodecDefault AVCodecDefault; + +struct AVSubtitle; + +/** + * AVCodec. + */ +typedef struct AVCodec { + /** + * Name of the codec implementation. + * The name is globally unique among encoders and among decoders (but an + * encoder and a decoder can share the same name). + * This is the primary way to find a codec from the user perspective. + */ + const char *name; + /** + * Descriptive name for the codec, meant to be more human readable than name. + * You should use the NULL_IF_CONFIG_SMALL() macro to define it. + */ + const char *long_name; + enum AVMediaType type; + enum AVCodecID id; + /** + * Codec capabilities. + * see AV_CODEC_CAP_* + */ + int capabilities; + const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0} + const enum AVPixelFormat *pix_fmts; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1 + const int *supported_samplerates; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0 + const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1 + const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0 + uint8_t max_lowres; ///< maximum value for lowres supported by the decoder + const AVClass *priv_class; ///< AVClass for the private context + const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN} + + /** + * Group name of the codec implementation. + * This is a short symbolic name of the wrapper backing this codec. A + * wrapper uses some kind of external implementation for the codec, such + * as an external library, or a codec implementation provided by the OS or + * the hardware. + * If this field is NULL, this is a builtin, libavcodec native codec. + * If non-NULL, this will be the suffix in AVCodec.name in most cases + * (usually AVCodec.name will be of the form "<codec_name>_<wrapper_name>"). + */ + const char *wrapper_name; + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavcodec and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + int priv_data_size; + struct AVCodec *next; + /** + * @name Frame-level threading support functions + * @{ + */ + /** + * If defined, called on thread contexts when they are created. + * If the codec allocates writable tables in init(), re-allocate them here. + * priv_data will be set to a copy of the original. + */ + int (*init_thread_copy)(AVCodecContext *); + /** + * Copy necessary context variables from a previous thread context to the current one. + * If not defined, the next thread will start automatically; otherwise, the codec + * must call ff_thread_finish_setup(). + * + * dst and src will (rarely) point to the same context, in which case memcpy should be skipped. + */ + int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src); + /** @} */ + + /** + * Private codec-specific defaults. + */ + const AVCodecDefault *defaults; + + /** + * Initialize codec static data, called from avcodec_register(). + * + * This is not intended for time consuming operations as it is + * run for every codec regardless of that codec being used. + */ + void (*init_static_data)(struct AVCodec *codec); + + int (*init)(AVCodecContext *); + int (*encode_sub)(AVCodecContext *, uint8_t *buf, int buf_size, + const struct AVSubtitle *sub); + /** + * Encode data to an AVPacket. + * + * @param avctx codec context + * @param avpkt output AVPacket (may contain a user-provided buffer) + * @param[in] frame AVFrame containing the raw data to be encoded + * @param[out] got_packet_ptr encoder sets to 0 or 1 to indicate that a + * non-empty packet was returned in avpkt. + * @return 0 on success, negative error code on failure + */ + int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, + int *got_packet_ptr); + int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt); + int (*close)(AVCodecContext *); + /** + * Encode API with decoupled packet/frame dataflow. The API is the + * same as the avcodec_ prefixed APIs (avcodec_send_frame() etc.), except + * that: + * - never called if the codec is closed or the wrong type, + * - if AV_CODEC_CAP_DELAY is not set, drain frames are never sent, + * - only one drain frame is ever passed down, + */ + int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame); + int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt); + + /** + * Decode API with decoupled packet/frame dataflow. This function is called + * to get one output frame. It should call ff_decode_get_packet() to obtain + * input data. + */ + int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame); + /** + * Flush buffers. + * Will be called when seeking + */ + void (*flush)(AVCodecContext *); + /** + * Internal codec capabilities. + * See FF_CODEC_CAP_* in internal.h + */ + int caps_internal; + + /** + * Decoding only, a comma-separated list of bitstream filters to apply to + * packets before decoding. + */ + const char *bsfs; + + /** + * Array of pointers to hardware configurations supported by the codec, + * or NULL if no hardware supported. The array is terminated by a NULL + * pointer. + * + * The user can only access this field via avcodec_get_hw_config(). + */ + const struct AVCodecHWConfigInternal **hw_configs; +} AVCodec; + +#if FF_API_CODEC_GET_SET +attribute_deprecated +int av_codec_get_max_lowres(const AVCodec *codec); +#endif + +struct MpegEncContext; + +/** + * Retrieve supported hardware configurations for a codec. + * + * Values of index from zero to some maximum return the indexed configuration + * descriptor; all other values return NULL. If the codec does not support + * any hardware configurations then it will always return NULL. + */ +const AVCodecHWConfig *avcodec_get_hw_config(const AVCodec *codec, int index); + +/** + * @defgroup lavc_hwaccel AVHWAccel + * + * @note Nothing in this structure should be accessed by the user. At some + * point in future it will not be externally visible at all. + * + * @{ + */ +typedef struct AVHWAccel { + /** + * Name of the hardware accelerated codec. + * The name is globally unique among encoders and among decoders (but an + * encoder and a decoder can share the same name). + */ + const char *name; + + /** + * Type of codec implemented by the hardware accelerator. + * + * See AVMEDIA_TYPE_xxx + */ + enum AVMediaType type; + + /** + * Codec implemented by the hardware accelerator. + * + * See AV_CODEC_ID_xxx + */ + enum AVCodecID id; + + /** + * Supported pixel format. + * + * Only hardware accelerated formats are supported here. + */ + enum AVPixelFormat pix_fmt; + + /** + * Hardware accelerated codec capabilities. + * see AV_HWACCEL_CODEC_CAP_* + */ + int capabilities; + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavcodec and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + + /** + * Allocate a custom buffer + */ + int (*alloc_frame)(AVCodecContext *avctx, AVFrame *frame); + + /** + * Called at the beginning of each frame or field picture. + * + * Meaningful frame information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * + * Note that buf can be NULL along with buf_size set to 0. + * Otherwise, this means the whole frame is available at this point. + * + * @param avctx the codec context + * @param buf the frame data buffer base + * @param buf_size the size of the frame in bytes + * @return zero if successful, a negative value otherwise + */ + int (*start_frame)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); + + /** + * Callback for parameter data (SPS/PPS/VPS etc). + * + * Useful for hardware decoders which keep persistent state about the + * video parameters, and need to receive any changes to update that state. + * + * @param avctx the codec context + * @param type the nal unit type + * @param buf the nal unit data buffer + * @param buf_size the size of the nal unit in bytes + * @return zero if successful, a negative value otherwise + */ + int (*decode_params)(AVCodecContext *avctx, int type, const uint8_t *buf, uint32_t buf_size); + + /** + * Callback for each slice. + * + * Meaningful slice information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * The only exception is XvMC, that works on MB level. + * + * @param avctx the codec context + * @param buf the slice data buffer base + * @param buf_size the size of the slice in bytes + * @return zero if successful, a negative value otherwise + */ + int (*decode_slice)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); + + /** + * Called at the end of each frame or field picture. + * + * The whole picture is parsed at this point and can now be sent + * to the hardware accelerator. This function is mandatory. + * + * @param avctx the codec context + * @return zero if successful, a negative value otherwise + */ + int (*end_frame)(AVCodecContext *avctx); + + /** + * Size of per-frame hardware accelerator private data. + * + * Private data is allocated with av_mallocz() before + * AVCodecContext.get_buffer() and deallocated after + * AVCodecContext.release_buffer(). + */ + int frame_priv_data_size; + + /** + * Called for every Macroblock in a slice. + * + * XvMC uses it to replace the ff_mpv_reconstruct_mb(). + * Instead of decoding to raw picture, MB parameters are + * stored in an array provided by the video driver. + * + * @param s the mpeg context + */ + void (*decode_mb)(struct MpegEncContext *s); + + /** + * Initialize the hwaccel private data. + * + * This will be called from ff_get_format(), after hwaccel and + * hwaccel_context are set and the hwaccel private data in AVCodecInternal + * is allocated. + */ + int (*init)(AVCodecContext *avctx); + + /** + * Uninitialize the hwaccel private data. + * + * This will be called from get_format() or avcodec_close(), after hwaccel + * and hwaccel_context are already uninitialized. + */ + int (*uninit)(AVCodecContext *avctx); + + /** + * Size of the private data to allocate in + * AVCodecInternal.hwaccel_priv_data. + */ + int priv_data_size; + + /** + * Internal hwaccel capabilities. + */ + int caps_internal; + + /** + * Fill the given hw_frames context with current codec parameters. Called + * from get_format. Refer to avcodec_get_hw_frames_parameters() for + * details. + * + * This CAN be called before AVHWAccel.init is called, and you must assume + * that avctx->hwaccel_priv_data is invalid. + */ + int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); +} AVHWAccel; + +/** + * HWAccel is experimental and is thus avoided in favor of non experimental + * codecs + */ +#define AV_HWACCEL_CODEC_CAP_EXPERIMENTAL 0x0200 + +/** + * Hardware acceleration should be used for decoding even if the codec level + * used is unknown or higher than the maximum supported level reported by the + * hardware driver. + * + * It's generally a good idea to pass this flag unless you have a specific + * reason not to, as hardware tends to under-report supported levels. + */ +#define AV_HWACCEL_FLAG_IGNORE_LEVEL (1 << 0) + +/** + * Hardware acceleration can output YUV pixel formats with a different chroma + * sampling than 4:2:0 and/or other than 8 bits per component. + */ +#define AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH (1 << 1) + +/** + * Hardware acceleration should still be attempted for decoding when the + * codec profile does not match the reported capabilities of the hardware. + * + * For example, this can be used to try to decode baseline profile H.264 + * streams in hardware - it will often succeed, because many streams marked + * as baseline profile actually conform to constrained baseline profile. + * + * @warning If the stream is actually not supported then the behaviour is + * undefined, and may include returning entirely incorrect output + * while indicating success. + */ +#define AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH (1 << 2) + +/** + * @} + */ + +#if FF_API_AVPICTURE +/** + * @defgroup lavc_picture AVPicture + * + * Functions for working with AVPicture + * @{ + */ + +/** + * Picture data structure. + * + * Up to four components can be stored into it, the last component is + * alpha. + * @deprecated use AVFrame or imgutils functions instead + */ +typedef struct AVPicture { + attribute_deprecated + uint8_t *data[AV_NUM_DATA_POINTERS]; ///< pointers to the image data planes + attribute_deprecated + int linesize[AV_NUM_DATA_POINTERS]; ///< number of bytes per line +} AVPicture; + +/** + * @} + */ +#endif + +enum AVSubtitleType { + SUBTITLE_NONE, + + SUBTITLE_BITMAP, ///< A bitmap, pict will be set + + /** + * Plain text, the text field must be set by the decoder and is + * authoritative. ass and pict fields may contain approximations. + */ + SUBTITLE_TEXT, + + /** + * Formatted text, the ass field must be set by the decoder and is + * authoritative. pict and text fields may contain approximations. + */ + SUBTITLE_ASS, +}; + +#define AV_SUBTITLE_FLAG_FORCED 0x00000001 + +typedef struct AVSubtitleRect { + int x; ///< top left corner of pict, undefined when pict is not set + int y; ///< top left corner of pict, undefined when pict is not set + int w; ///< width of pict, undefined when pict is not set + int h; ///< height of pict, undefined when pict is not set + int nb_colors; ///< number of colors in pict, undefined when pict is not set + +#if FF_API_AVPICTURE + /** + * @deprecated unused + */ + attribute_deprecated + AVPicture pict; +#endif + /** + * data+linesize for the bitmap of this subtitle. + * Can be set for text/ass as well once they are rendered. + */ + uint8_t *data[4]; + int linesize[4]; + + enum AVSubtitleType type; + + char *text; ///< 0 terminated plain UTF-8 text + + /** + * 0 terminated ASS/SSA compatible event line. + * The presentation of this is unaffected by the other values in this + * struct. + */ + char *ass; + + int flags; +} AVSubtitleRect; + +typedef struct AVSubtitle { + uint16_t format; /* 0 = graphics */ + uint32_t start_display_time; /* relative to packet pts, in ms */ + uint32_t end_display_time; /* relative to packet pts, in ms */ + unsigned num_rects; + AVSubtitleRect **rects; + int64_t pts; ///< Same as packet pts, in AV_TIME_BASE +} AVSubtitle; + +/** + * This struct describes the properties of an encoded stream. + * + * sizeof(AVCodecParameters) is not a part of the public ABI, this struct must + * be allocated with avcodec_parameters_alloc() and freed with + * avcodec_parameters_free(). + */ +typedef struct AVCodecParameters { + /** + * General type of the encoded data. + */ + enum AVMediaType codec_type; + /** + * Specific type of the encoded data (the codec used). + */ + enum AVCodecID codec_id; + /** + * Additional information about the codec (corresponds to the AVI FOURCC). + */ + uint32_t codec_tag; + + /** + * Extra binary data needed for initializing the decoder, codec-dependent. + * + * Must be allocated with av_malloc() and will be freed by + * avcodec_parameters_free(). The allocated size of extradata must be at + * least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding + * bytes zeroed. + */ + uint8_t *extradata; + /** + * Size of the extradata content in bytes. + */ + int extradata_size; + + /** + * - video: the pixel format, the value corresponds to enum AVPixelFormat. + * - audio: the sample format, the value corresponds to enum AVSampleFormat. + */ + int format; + + /** + * The average bitrate of the encoded data (in bits per second). + */ + int64_t bit_rate; + + /** + * The number of bits per sample in the codedwords. + * + * This is basically the bitrate per sample. It is mandatory for a bunch of + * formats to actually decode them. It's the number of bits for one sample in + * the actual coded bitstream. + * + * This could be for example 4 for ADPCM + * For PCM formats this matches bits_per_raw_sample + * Can be 0 + */ + int bits_per_coded_sample; + + /** + * This is the number of valid bits in each output sample. If the + * sample format has more bits, the least significant bits are additional + * padding bits, which are always 0. Use right shifts to reduce the sample + * to its actual size. For example, audio formats with 24 bit samples will + * have bits_per_raw_sample set to 24, and format set to AV_SAMPLE_FMT_S32. + * To get the original sample use "(int32_t)sample >> 8"." + * + * For ADPCM this might be 12 or 16 or similar + * Can be 0 + */ + int bits_per_raw_sample; + + /** + * Codec-specific bitstream restrictions that the stream conforms to. + */ + int profile; + int level; + + /** + * Video only. The dimensions of the video frame in pixels. + */ + int width; + int height; + + /** + * Video only. The aspect ratio (width / height) which a single pixel + * should have when displayed. + * + * When the aspect ratio is unknown / undefined, the numerator should be + * set to 0 (the denominator may have any value). + */ + AVRational sample_aspect_ratio; + + /** + * Video only. The order of the fields in interlaced video. + */ + enum AVFieldOrder field_order; + + /** + * Video only. Additional colorspace characteristics. + */ + enum AVColorRange color_range; + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_trc; + enum AVColorSpace color_space; + enum AVChromaLocation chroma_location; + + /** + * Video only. Number of delayed frames. + */ + int video_delay; + + /** + * Audio only. The channel layout bitmask. May be 0 if the channel layout is + * unknown or unspecified, otherwise the number of bits set must be equal to + * the channels field. + */ + uint64_t channel_layout; + /** + * Audio only. The number of audio channels. + */ + int channels; + /** + * Audio only. The number of audio samples per second. + */ + int sample_rate; + /** + * Audio only. The number of bytes per coded audio frame, required by some + * formats. + * + * Corresponds to nBlockAlign in WAVEFORMATEX. + */ + int block_align; + /** + * Audio only. Audio frame size, if known. Required by some formats to be static. + */ + int frame_size; + + /** + * Audio only. The amount of padding (in samples) inserted by the encoder at + * the beginning of the audio. I.e. this number of leading decoded samples + * must be discarded by the caller to get the original audio without leading + * padding. + */ + int initial_padding; + /** + * Audio only. The amount of padding (in samples) appended by the encoder to + * the end of the audio. I.e. this number of decoded samples must be + * discarded by the caller from the end of the stream to get the original + * audio without any trailing padding. + */ + int trailing_padding; + /** + * Audio only. Number of samples to skip after a discontinuity. + */ + int seek_preroll; +} AVCodecParameters; + +/** + * Iterate over all registered codecs. + * + * @param opaque a pointer where libavcodec will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered codec or NULL when the iteration is + * finished + */ +const AVCodec *av_codec_iterate(void **opaque); + +#if FF_API_NEXT +/** + * If c is NULL, returns the first registered codec, + * if c is non-NULL, returns the next registered codec after c, + * or NULL if c is the last one. + */ +attribute_deprecated +AVCodec *av_codec_next(const AVCodec *c); +#endif + +/** + * Return the LIBAVCODEC_VERSION_INT constant. + */ +unsigned avcodec_version(void); + +/** + * Return the libavcodec build-time configuration. + */ +const char *avcodec_configuration(void); + +/** + * Return the libavcodec license. + */ +const char *avcodec_license(void); + +#if FF_API_NEXT +/** + * Register the codec codec and initialize libavcodec. + * + * @warning either this function or avcodec_register_all() must be called + * before any other libavcodec functions. + * + * @see avcodec_register_all() + */ +attribute_deprecated +void avcodec_register(AVCodec *codec); + +/** + * Register all the codecs, parsers and bitstream filters which were enabled at + * configuration time. If you do not call this function you can select exactly + * which formats you want to support, by using the individual registration + * functions. + * + * @see avcodec_register + * @see av_register_codec_parser + * @see av_register_bitstream_filter + */ +attribute_deprecated +void avcodec_register_all(void); +#endif + +/** + * Allocate an AVCodecContext and set its fields to default values. The + * resulting struct should be freed with avcodec_free_context(). + * + * @param codec if non-NULL, allocate private data and initialize defaults + * for the given codec. It is illegal to then call avcodec_open2() + * with a different codec. + * If NULL, then the codec-specific defaults won't be initialized, + * which may result in suboptimal default settings (this is + * important mainly for encoders, e.g. libx264). + * + * @return An AVCodecContext filled with default values or NULL on failure. + */ +AVCodecContext *avcodec_alloc_context3(const AVCodec *codec); + +/** + * Free the codec context and everything associated with it and write NULL to + * the provided pointer. + */ +void avcodec_free_context(AVCodecContext **avctx); + +#if FF_API_GET_CONTEXT_DEFAULTS +/** + * @deprecated This function should not be used, as closing and opening a codec + * context multiple time is not supported. A new codec context should be + * allocated for each new use. + */ +int avcodec_get_context_defaults3(AVCodecContext *s, const AVCodec *codec); +#endif + +/** + * Get the AVClass for AVCodecContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *avcodec_get_class(void); + +#if FF_API_COPY_CONTEXT +/** + * Get the AVClass for AVFrame. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *avcodec_get_frame_class(void); + +/** + * Get the AVClass for AVSubtitleRect. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *avcodec_get_subtitle_rect_class(void); + +/** + * Copy the settings of the source AVCodecContext into the destination + * AVCodecContext. The resulting destination codec context will be + * unopened, i.e. you are required to call avcodec_open2() before you + * can use this AVCodecContext to decode/encode video/audio data. + * + * @param dest target codec context, should be initialized with + * avcodec_alloc_context3(NULL), but otherwise uninitialized + * @param src source codec context + * @return AVERROR() on error (e.g. memory allocation error), 0 on success + * + * @deprecated The semantics of this function are ill-defined and it should not + * be used. If you need to transfer the stream parameters from one codec context + * to another, use an intermediate AVCodecParameters instance and the + * avcodec_parameters_from_context() / avcodec_parameters_to_context() + * functions. + */ +attribute_deprecated +int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src); +#endif + +/** + * Allocate a new AVCodecParameters and set its fields to default values + * (unknown/invalid/0). The returned struct must be freed with + * avcodec_parameters_free(). + */ +AVCodecParameters *avcodec_parameters_alloc(void); + +/** + * Free an AVCodecParameters instance and everything associated with it and + * write NULL to the supplied pointer. + */ +void avcodec_parameters_free(AVCodecParameters **par); + +/** + * Copy the contents of src to dst. Any allocated fields in dst are freed and + * replaced with newly allocated duplicates of the corresponding fields in src. + * + * @return >= 0 on success, a negative AVERROR code on failure. + */ +int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src); + +/** + * Fill the parameters struct based on the values from the supplied codec + * context. Any allocated fields in par are freed and replaced with duplicates + * of the corresponding fields in codec. + * + * @return >= 0 on success, a negative AVERROR code on failure + */ +int avcodec_parameters_from_context(AVCodecParameters *par, + const AVCodecContext *codec); + +/** + * Fill the codec context based on the values from the supplied codec + * parameters. Any allocated fields in codec that have a corresponding field in + * par are freed and replaced with duplicates of the corresponding field in par. + * Fields in codec that do not have a counterpart in par are not touched. + * + * @return >= 0 on success, a negative AVERROR code on failure. + */ +int avcodec_parameters_to_context(AVCodecContext *codec, + const AVCodecParameters *par); + +/** + * Initialize the AVCodecContext to use the given AVCodec. Prior to using this + * function the context has to be allocated with avcodec_alloc_context3(). + * + * The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), + * avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for + * retrieving a codec. + * + * @warning This function is not thread safe! + * + * @note Always call this function before using decoding routines (such as + * @ref avcodec_receive_frame()). + * + * @code + * avcodec_register_all(); + * av_dict_set(&opts, "b", "2.5M", 0); + * codec = avcodec_find_decoder(AV_CODEC_ID_H264); + * if (!codec) + * exit(1); + * + * context = avcodec_alloc_context3(codec); + * + * if (avcodec_open2(context, codec, opts) < 0) + * exit(1); + * @endcode + * + * @param avctx The context to initialize. + * @param codec The codec to open this context for. If a non-NULL codec has been + * previously passed to avcodec_alloc_context3() or + * for this context, then this parameter MUST be either NULL or + * equal to the previously passed codec. + * @param options A dictionary filled with AVCodecContext and codec-private options. + * On return this object will be filled with options that were not found. + * + * @return zero on success, a negative value on error + * @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(), + * av_dict_set(), av_opt_find(). + */ +int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); + +/** + * Close a given AVCodecContext and free all the data associated with it + * (but not the AVCodecContext itself). + * + * Calling this function on an AVCodecContext that hasn't been opened will free + * the codec-specific data allocated in avcodec_alloc_context3() with a non-NULL + * codec. Subsequent calls will do nothing. + * + * @note Do not use this function. Use avcodec_free_context() to destroy a + * codec context (either open or closed). Opening and closing a codec context + * multiple times is not supported anymore -- use multiple codec contexts + * instead. + */ +int avcodec_close(AVCodecContext *avctx); + +/** + * Free all allocated data in the given subtitle struct. + * + * @param sub AVSubtitle to free. + */ +void avsubtitle_free(AVSubtitle *sub); + +/** + * @} + */ + +/** + * @addtogroup lavc_packet + * @{ + */ + +/** + * Allocate an AVPacket and set its fields to default values. The resulting + * struct must be freed using av_packet_free(). + * + * @return An AVPacket filled with default values or NULL on failure. + * + * @note this only allocates the AVPacket itself, not the data buffers. Those + * must be allocated through other means such as av_new_packet. + * + * @see av_new_packet + */ +AVPacket *av_packet_alloc(void); + +/** + * Create a new packet that references the same data as src. + * + * This is a shortcut for av_packet_alloc()+av_packet_ref(). + * + * @return newly created AVPacket on success, NULL on error. + * + * @see av_packet_alloc + * @see av_packet_ref + */ +AVPacket *av_packet_clone(const AVPacket *src); + +/** + * Free the packet, if the packet is reference counted, it will be + * unreferenced first. + * + * @param pkt packet to be freed. The pointer will be set to NULL. + * @note passing NULL is a no-op. + */ +void av_packet_free(AVPacket **pkt); + +/** + * Initialize optional fields of a packet with default values. + * + * Note, this does not touch the data and size members, which have to be + * initialized separately. + * + * @param pkt packet + */ +void av_init_packet(AVPacket *pkt); + +/** + * Allocate the payload of a packet and initialize its fields with + * default values. + * + * @param pkt packet + * @param size wanted payload size + * @return 0 if OK, AVERROR_xxx otherwise + */ +int av_new_packet(AVPacket *pkt, int size); + +/** + * Reduce packet size, correctly zeroing padding + * + * @param pkt packet + * @param size new size + */ +void av_shrink_packet(AVPacket *pkt, int size); + +/** + * Increase packet size, correctly zeroing padding + * + * @param pkt packet + * @param grow_by number of bytes by which to increase the size of the packet + */ +int av_grow_packet(AVPacket *pkt, int grow_by); + +/** + * Initialize a reference-counted packet from av_malloc()ed data. + * + * @param pkt packet to be initialized. This function will set the data, size, + * buf and destruct fields, all others are left untouched. + * @param data Data allocated by av_malloc() to be used as packet data. If this + * function returns successfully, the data is owned by the underlying AVBuffer. + * The caller may not access the data through other means. + * @param size size of data in bytes, without the padding. I.e. the full buffer + * size is assumed to be size + AV_INPUT_BUFFER_PADDING_SIZE. + * + * @return 0 on success, a negative AVERROR on error + */ +int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size); + +#if FF_API_AVPACKET_OLD_API +/** + * @warning This is a hack - the packet memory allocation stuff is broken. The + * packet is allocated if it was not really allocated. + * + * @deprecated Use av_packet_ref or av_packet_make_refcounted + */ +attribute_deprecated +int av_dup_packet(AVPacket *pkt); +/** + * Copy packet, including contents + * + * @return 0 on success, negative AVERROR on fail + * + * @deprecated Use av_packet_ref + */ +attribute_deprecated +int av_copy_packet(AVPacket *dst, const AVPacket *src); + +/** + * Copy packet side data + * + * @return 0 on success, negative AVERROR on fail + * + * @deprecated Use av_packet_copy_props + */ +attribute_deprecated +int av_copy_packet_side_data(AVPacket *dst, const AVPacket *src); + +/** + * Free a packet. + * + * @deprecated Use av_packet_unref + * + * @param pkt packet to free + */ +attribute_deprecated +void av_free_packet(AVPacket *pkt); +#endif +/** + * Allocate new information of a packet. + * + * @param pkt packet + * @param type side information type + * @param size side information size + * @return pointer to fresh allocated data or NULL otherwise + */ +uint8_t* av_packet_new_side_data(AVPacket *pkt, enum AVPacketSideDataType type, + int size); + +/** + * Wrap an existing array as a packet side data. + * + * @param pkt packet + * @param type side information type + * @param data the side data array. It must be allocated with the av_malloc() + * family of functions. The ownership of the data is transferred to + * pkt. + * @param size side information size + * @return a non-negative number on success, a negative AVERROR code on + * failure. On failure, the packet is unchanged and the data remains + * owned by the caller. + */ +int av_packet_add_side_data(AVPacket *pkt, enum AVPacketSideDataType type, + uint8_t *data, size_t size); + +/** + * Shrink the already allocated side data buffer + * + * @param pkt packet + * @param type side information type + * @param size new side information size + * @return 0 on success, < 0 on failure + */ +int av_packet_shrink_side_data(AVPacket *pkt, enum AVPacketSideDataType type, + int size); + +/** + * Get side information from packet. + * + * @param pkt packet + * @param type desired side information type + * @param size pointer for side information size to store (optional) + * @return pointer to data if present or NULL otherwise + */ +uint8_t* av_packet_get_side_data(const AVPacket *pkt, enum AVPacketSideDataType type, + int *size); + +#if FF_API_MERGE_SD_API +attribute_deprecated +int av_packet_merge_side_data(AVPacket *pkt); + +attribute_deprecated +int av_packet_split_side_data(AVPacket *pkt); +#endif + +const char *av_packet_side_data_name(enum AVPacketSideDataType type); + +/** + * Pack a dictionary for use in side_data. + * + * @param dict The dictionary to pack. + * @param size pointer to store the size of the returned data + * @return pointer to data if successful, NULL otherwise + */ +uint8_t *av_packet_pack_dictionary(AVDictionary *dict, int *size); +/** + * Unpack a dictionary from side_data. + * + * @param data data from side_data + * @param size size of the data + * @param dict the metadata storage dictionary + * @return 0 on success, < 0 on failure + */ +int av_packet_unpack_dictionary(const uint8_t *data, int size, AVDictionary **dict); + + +/** + * Convenience function to free all the side data stored. + * All the other fields stay untouched. + * + * @param pkt packet + */ +void av_packet_free_side_data(AVPacket *pkt); + +/** + * Setup a new reference to the data described by a given packet + * + * If src is reference-counted, setup dst as a new reference to the + * buffer in src. Otherwise allocate a new buffer in dst and copy the + * data from src into it. + * + * All the other fields are copied from src. + * + * @see av_packet_unref + * + * @param dst Destination packet + * @param src Source packet + * + * @return 0 on success, a negative AVERROR on error. + */ +int av_packet_ref(AVPacket *dst, const AVPacket *src); + +/** + * Wipe the packet. + * + * Unreference the buffer referenced by the packet and reset the + * remaining packet fields to their default values. + * + * @param pkt The packet to be unreferenced. + */ +void av_packet_unref(AVPacket *pkt); + +/** + * Move every field in src to dst and reset src. + * + * @see av_packet_unref + * + * @param src Source packet, will be reset + * @param dst Destination packet + */ +void av_packet_move_ref(AVPacket *dst, AVPacket *src); + +/** + * Copy only "properties" fields from src to dst. + * + * Properties for the purpose of this function are all the fields + * beside those related to the packet data (buf, data, size) + * + * @param dst Destination packet + * @param src Source packet + * + * @return 0 on success AVERROR on failure. + */ +int av_packet_copy_props(AVPacket *dst, const AVPacket *src); + +/** + * Ensure the data described by a given packet is reference counted. + * + * @note This function does not ensure that the reference will be writable. + * Use av_packet_make_writable instead for that purpose. + * + * @see av_packet_ref + * @see av_packet_make_writable + * + * @param pkt packet whose data should be made reference counted. + * + * @return 0 on success, a negative AVERROR on error. On failure, the + * packet is unchanged. + */ +int av_packet_make_refcounted(AVPacket *pkt); + +/** + * Create a writable reference for the data described by a given packet, + * avoiding data copy if possible. + * + * @param pkt Packet whose data should be made writable. + * + * @return 0 on success, a negative AVERROR on failure. On failure, the + * packet is unchanged. + */ +int av_packet_make_writable(AVPacket *pkt); + +/** + * Convert valid timing fields (timestamps / durations) in a packet from one + * timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be + * ignored. + * + * @param pkt packet on which the conversion will be performed + * @param tb_src source timebase, in which the timing fields in pkt are + * expressed + * @param tb_dst destination timebase, to which the timing fields will be + * converted + */ +void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst); + +/** + * @} + */ + +/** + * @addtogroup lavc_decoding + * @{ + */ + +/** + * Find a registered decoder with a matching codec ID. + * + * @param id AVCodecID of the requested decoder + * @return A decoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_decoder(enum AVCodecID id); + +/** + * Find a registered decoder with the specified name. + * + * @param name name of the requested decoder + * @return A decoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_decoder_by_name(const char *name); + +/** + * The default callback for AVCodecContext.get_buffer2(). It is made public so + * it can be called by custom get_buffer2() implementations for decoders without + * AV_CODEC_CAP_DR1 set. + */ +int avcodec_default_get_buffer2(AVCodecContext *s, AVFrame *frame, int flags); + +/** + * Modify width and height values so that they will result in a memory + * buffer that is acceptable for the codec if you do not use any horizontal + * padding. + * + * May only be used if a codec with AV_CODEC_CAP_DR1 has been opened. + */ +void avcodec_align_dimensions(AVCodecContext *s, int *width, int *height); + +/** + * Modify width and height values so that they will result in a memory + * buffer that is acceptable for the codec if you also ensure that all + * line sizes are a multiple of the respective linesize_align[i]. + * + * May only be used if a codec with AV_CODEC_CAP_DR1 has been opened. + */ +void avcodec_align_dimensions2(AVCodecContext *s, int *width, int *height, + int linesize_align[AV_NUM_DATA_POINTERS]); + +/** + * Converts AVChromaLocation to swscale x/y chroma position. + * + * The positions represent the chroma (0,0) position in a coordinates system + * with luma (0,0) representing the origin and luma(1,1) representing 256,256 + * + * @param xpos horizontal chroma sample position + * @param ypos vertical chroma sample position + */ +int avcodec_enum_to_chroma_pos(int *xpos, int *ypos, enum AVChromaLocation pos); + +/** + * Converts swscale x/y chroma position to AVChromaLocation. + * + * The positions represent the chroma (0,0) position in a coordinates system + * with luma (0,0) representing the origin and luma(1,1) representing 256,256 + * + * @param xpos horizontal chroma sample position + * @param ypos vertical chroma sample position + */ +enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos); + +/** + * Decode the audio frame of size avpkt->size from avpkt->data into frame. + * + * Some decoders may support multiple frames in a single AVPacket. Such + * decoders would then just decode the first frame and the return value would be + * less than the packet size. In this case, avcodec_decode_audio4 has to be + * called again with an AVPacket containing the remaining data in order to + * decode the second frame, etc... Even if no frames are returned, the packet + * needs to be fed to the decoder with remaining data until it is completely + * consumed or an error occurs. + * + * Some decoders (those marked with AV_CODEC_CAP_DELAY) have a delay between input + * and output. This means that for some packets they will not immediately + * produce decoded output and need to be flushed at the end of decoding to get + * all the decoded data. Flushing is done by calling this function with packets + * with avpkt->data set to NULL and avpkt->size set to 0 until it stops + * returning samples. It is safe to flush even those decoders that are not + * marked with AV_CODEC_CAP_DELAY, then no samples will be returned. + * + * @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE + * larger than the actual read bytes because some optimized bitstream + * readers read 32 or 64 bits at once and could read over the end. + * + * @note The AVCodecContext MUST have been opened with @ref avcodec_open2() + * before packets may be fed to the decoder. + * + * @param avctx the codec context + * @param[out] frame The AVFrame in which to store decoded audio samples. + * The decoder will allocate a buffer for the decoded frame by + * calling the AVCodecContext.get_buffer2() callback. + * When AVCodecContext.refcounted_frames is set to 1, the frame is + * reference counted and the returned reference belongs to the + * caller. The caller must release the frame using av_frame_unref() + * when the frame is no longer needed. The caller may safely write + * to the frame if av_frame_is_writable() returns 1. + * When AVCodecContext.refcounted_frames is set to 0, the returned + * reference belongs to the decoder and is valid only until the + * next call to this function or until closing or flushing the + * decoder. The caller may not write to it. + * @param[out] got_frame_ptr Zero if no frame could be decoded, otherwise it is + * non-zero. Note that this field being set to zero + * does not mean that an error has occurred. For + * decoders with AV_CODEC_CAP_DELAY set, no given decode + * call is guaranteed to produce a frame. + * @param[in] avpkt The input AVPacket containing the input buffer. + * At least avpkt->data and avpkt->size should be set. Some + * decoders might also require additional fields to be set. + * @return A negative error code is returned if an error occurred during + * decoding, otherwise the number of bytes consumed from the input + * AVPacket is returned. + * +* @deprecated Use avcodec_send_packet() and avcodec_receive_frame(). + */ +attribute_deprecated +int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, + int *got_frame_ptr, const AVPacket *avpkt); + +/** + * Decode the video frame of size avpkt->size from avpkt->data into picture. + * Some decoders may support multiple frames in a single AVPacket, such + * decoders would then just decode the first frame. + * + * @warning The input buffer must be AV_INPUT_BUFFER_PADDING_SIZE larger than + * the actual read bytes because some optimized bitstream readers read 32 or 64 + * bits at once and could read over the end. + * + * @warning The end of the input buffer buf should be set to 0 to ensure that + * no overreading happens for damaged MPEG streams. + * + * @note Codecs which have the AV_CODEC_CAP_DELAY capability set have a delay + * between input and output, these need to be fed with avpkt->data=NULL, + * avpkt->size=0 at the end to return the remaining frames. + * + * @note The AVCodecContext MUST have been opened with @ref avcodec_open2() + * before packets may be fed to the decoder. + * + * @param avctx the codec context + * @param[out] picture The AVFrame in which the decoded video frame will be stored. + * Use av_frame_alloc() to get an AVFrame. The codec will + * allocate memory for the actual bitmap by calling the + * AVCodecContext.get_buffer2() callback. + * When AVCodecContext.refcounted_frames is set to 1, the frame is + * reference counted and the returned reference belongs to the + * caller. The caller must release the frame using av_frame_unref() + * when the frame is no longer needed. The caller may safely write + * to the frame if av_frame_is_writable() returns 1. + * When AVCodecContext.refcounted_frames is set to 0, the returned + * reference belongs to the decoder and is valid only until the + * next call to this function or until closing or flushing the + * decoder. The caller may not write to it. + * + * @param[in] avpkt The input AVPacket containing the input buffer. + * You can create such packet with av_init_packet() and by then setting + * data and size, some decoders might in addition need other fields like + * flags&AV_PKT_FLAG_KEY. All decoders are designed to use the least + * fields possible. + * @param[in,out] got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero. + * @return On error a negative value is returned, otherwise the number of bytes + * used or zero if no frame could be decompressed. + * + * @deprecated Use avcodec_send_packet() and avcodec_receive_frame(). + */ +attribute_deprecated +int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, + const AVPacket *avpkt); + +/** + * Decode a subtitle message. + * Return a negative value on error, otherwise return the number of bytes used. + * If no subtitle could be decompressed, got_sub_ptr is zero. + * Otherwise, the subtitle is stored in *sub. + * Note that AV_CODEC_CAP_DR1 is not available for subtitle codecs. This is for + * simplicity, because the performance difference is expect to be negligible + * and reusing a get_buffer written for video codecs would probably perform badly + * due to a potentially very different allocation pattern. + * + * Some decoders (those marked with AV_CODEC_CAP_DELAY) have a delay between input + * and output. This means that for some packets they will not immediately + * produce decoded output and need to be flushed at the end of decoding to get + * all the decoded data. Flushing is done by calling this function with packets + * with avpkt->data set to NULL and avpkt->size set to 0 until it stops + * returning subtitles. It is safe to flush even those decoders that are not + * marked with AV_CODEC_CAP_DELAY, then no subtitles will be returned. + * + * @note The AVCodecContext MUST have been opened with @ref avcodec_open2() + * before packets may be fed to the decoder. + * + * @param avctx the codec context + * @param[out] sub The Preallocated AVSubtitle in which the decoded subtitle will be stored, + * must be freed with avsubtitle_free if *got_sub_ptr is set. + * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero. + * @param[in] avpkt The input AVPacket containing the input buffer. + */ +int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, + int *got_sub_ptr, + AVPacket *avpkt); + +/** + * Supply raw packet data as input to a decoder. + * + * Internally, this call will copy relevant AVCodecContext fields, which can + * influence decoding per-packet, and apply them when the packet is actually + * decoded. (For example AVCodecContext.skip_frame, which might direct the + * decoder to drop the frame contained by the packet sent with this function.) + * + * @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE + * larger than the actual read bytes because some optimized bitstream + * readers read 32 or 64 bits at once and could read over the end. + * + * @warning Do not mix this API with the legacy API (like avcodec_decode_video2()) + * on the same AVCodecContext. It will return unexpected results now + * or in future libavcodec versions. + * + * @note The AVCodecContext MUST have been opened with @ref avcodec_open2() + * before packets may be fed to the decoder. + * + * @param avctx codec context + * @param[in] avpkt The input AVPacket. Usually, this will be a single video + * frame, or several complete audio frames. + * Ownership of the packet remains with the caller, and the + * decoder will not write to the packet. The decoder may create + * a reference to the packet data (or copy it if the packet is + * not reference-counted). + * Unlike with older APIs, the packet is always fully consumed, + * and if it contains multiple frames (e.g. some audio codecs), + * will require you to call avcodec_receive_frame() multiple + * times afterwards before you can send a new packet. + * It can be NULL (or an AVPacket with data set to NULL and + * size set to 0); in this case, it is considered a flush + * packet, which signals the end of the stream. Sending the + * first flush packet will return success. Subsequent ones are + * unnecessary and will return AVERROR_EOF. If the decoder + * still has frames buffered, it will return them after sending + * a flush packet. + * + * @return 0 on success, otherwise negative error code: + * AVERROR(EAGAIN): input is not accepted in the current state - user + * must read output with avcodec_receive_frame() (once + * all output is read, the packet should be resent, and + * the call will not fail with EAGAIN). + * AVERROR_EOF: the decoder has been flushed, and no new packets can + * be sent to it (also returned if more than 1 flush + * packet is sent) + * AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush + * AVERROR(ENOMEM): failed to add packet to internal queue, or similar + * other errors: legitimate decoding errors + */ +int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); + +/** + * Return decoded output data from a decoder. + * + * @param avctx codec context + * @param frame This will be set to a reference-counted video or audio + * frame (depending on the decoder type) allocated by the + * decoder. Note that the function will always call + * av_frame_unref(frame) before doing anything else. + * + * @return + * 0: success, a frame was returned + * AVERROR(EAGAIN): output is not available in this state - user must try + * to send new input + * AVERROR_EOF: the decoder has been fully flushed, and there will be + * no more output frames + * AVERROR(EINVAL): codec not opened, or it is an encoder + * other negative values: legitimate decoding errors + */ +int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame); + +/** + * Supply a raw video or audio frame to the encoder. Use avcodec_receive_packet() + * to retrieve buffered output packets. + * + * @param avctx codec context + * @param[in] frame AVFrame containing the raw audio or video frame to be encoded. + * Ownership of the frame remains with the caller, and the + * encoder will not write to the frame. The encoder may create + * a reference to the frame data (or copy it if the frame is + * not reference-counted). + * It can be NULL, in which case it is considered a flush + * packet. This signals the end of the stream. If the encoder + * still has packets buffered, it will return them after this + * call. Once flushing mode has been entered, additional flush + * packets are ignored, and sending frames will return + * AVERROR_EOF. + * + * For audio: + * If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame + * can have any number of samples. + * If it is not set, frame->nb_samples must be equal to + * avctx->frame_size for all frames except the last. + * The final frame may be smaller than avctx->frame_size. + * @return 0 on success, otherwise negative error code: + * AVERROR(EAGAIN): input is not accepted in the current state - user + * must read output with avcodec_receive_packet() (once + * all output is read, the packet should be resent, and + * the call will not fail with EAGAIN). + * AVERROR_EOF: the encoder has been flushed, and no new frames can + * be sent to it + * AVERROR(EINVAL): codec not opened, refcounted_frames not set, it is a + * decoder, or requires flush + * AVERROR(ENOMEM): failed to add packet to internal queue, or similar + * other errors: legitimate decoding errors + */ +int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame); + +/** + * Read encoded data from the encoder. + * + * @param avctx codec context + * @param avpkt This will be set to a reference-counted packet allocated by the + * encoder. Note that the function will always call + * av_frame_unref(frame) before doing anything else. + * @return 0 on success, otherwise negative error code: + * AVERROR(EAGAIN): output is not available in the current state - user + * must try to send input + * AVERROR_EOF: the encoder has been fully flushed, and there will be + * no more output packets + * AVERROR(EINVAL): codec not opened, or it is an encoder + * other errors: legitimate decoding errors + */ +int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt); + +/** + * Create and return a AVHWFramesContext with values adequate for hardware + * decoding. This is meant to get called from the get_format callback, and is + * a helper for preparing a AVHWFramesContext for AVCodecContext.hw_frames_ctx. + * This API is for decoding with certain hardware acceleration modes/APIs only. + * + * The returned AVHWFramesContext is not initialized. The caller must do this + * with av_hwframe_ctx_init(). + * + * Calling this function is not a requirement, but makes it simpler to avoid + * codec or hardware API specific details when manually allocating frames. + * + * Alternatively to this, an API user can set AVCodecContext.hw_device_ctx, + * which sets up AVCodecContext.hw_frames_ctx fully automatically, and makes + * it unnecessary to call this function or having to care about + * AVHWFramesContext initialization at all. + * + * There are a number of requirements for calling this function: + * + * - It must be called from get_format with the same avctx parameter that was + * passed to get_format. Calling it outside of get_format is not allowed, and + * can trigger undefined behavior. + * - The function is not always supported (see description of return values). + * Even if this function returns successfully, hwaccel initialization could + * fail later. (The degree to which implementations check whether the stream + * is actually supported varies. Some do this check only after the user's + * get_format callback returns.) + * - The hw_pix_fmt must be one of the choices suggested by get_format. If the + * user decides to use a AVHWFramesContext prepared with this API function, + * the user must return the same hw_pix_fmt from get_format. + * - The device_ref passed to this function must support the given hw_pix_fmt. + * - After calling this API function, it is the user's responsibility to + * initialize the AVHWFramesContext (returned by the out_frames_ref parameter), + * and to set AVCodecContext.hw_frames_ctx to it. If done, this must be done + * before returning from get_format (this is implied by the normal + * AVCodecContext.hw_frames_ctx API rules). + * - The AVHWFramesContext parameters may change every time time get_format is + * called. Also, AVCodecContext.hw_frames_ctx is reset before get_format. So + * you are inherently required to go through this process again on every + * get_format call. + * - It is perfectly possible to call this function without actually using + * the resulting AVHWFramesContext. One use-case might be trying to reuse a + * previously initialized AVHWFramesContext, and calling this API function + * only to test whether the required frame parameters have changed. + * - Fields that use dynamically allocated values of any kind must not be set + * by the user unless setting them is explicitly allowed by the documentation. + * If the user sets AVHWFramesContext.free and AVHWFramesContext.user_opaque, + * the new free callback must call the potentially set previous free callback. + * This API call may set any dynamically allocated fields, including the free + * callback. + * + * The function will set at least the following fields on AVHWFramesContext + * (potentially more, depending on hwaccel API): + * + * - All fields set by av_hwframe_ctx_alloc(). + * - Set the format field to hw_pix_fmt. + * - Set the sw_format field to the most suited and most versatile format. (An + * implication is that this will prefer generic formats over opaque formats + * with arbitrary restrictions, if possible.) + * - Set the width/height fields to the coded frame size, rounded up to the + * API-specific minimum alignment. + * - Only _if_ the hwaccel requires a pre-allocated pool: set the initial_pool_size + * field to the number of maximum reference surfaces possible with the codec, + * plus 1 surface for the user to work (meaning the user can safely reference + * at most 1 decoded surface at a time), plus additional buffering introduced + * by frame threading. If the hwaccel does not require pre-allocation, the + * field is left to 0, and the decoder will allocate new surfaces on demand + * during decoding. + * - Possibly AVHWFramesContext.hwctx fields, depending on the underlying + * hardware API. + * + * Essentially, out_frames_ref returns the same as av_hwframe_ctx_alloc(), but + * with basic frame parameters set. + * + * The function is stateless, and does not change the AVCodecContext or the + * device_ref AVHWDeviceContext. + * + * @param avctx The context which is currently calling get_format, and which + * implicitly contains all state needed for filling the returned + * AVHWFramesContext properly. + * @param device_ref A reference to the AVHWDeviceContext describing the device + * which will be used by the hardware decoder. + * @param hw_pix_fmt The hwaccel format you are going to return from get_format. + * @param out_frames_ref On success, set to a reference to an _uninitialized_ + * AVHWFramesContext, created from the given device_ref. + * Fields will be set to values required for decoding. + * Not changed if an error is returned. + * @return zero on success, a negative value on error. The following error codes + * have special semantics: + * AVERROR(ENOENT): the decoder does not support this functionality. Setup + * is always manual, or it is a decoder which does not + * support setting AVCodecContext.hw_frames_ctx at all, + * or it is a software format. + * AVERROR(EINVAL): it is known that hardware decoding is not supported for + * this configuration, or the device_ref is not supported + * for the hwaccel referenced by hw_pix_fmt. + */ +int avcodec_get_hw_frames_parameters(AVCodecContext *avctx, + AVBufferRef *device_ref, + enum AVPixelFormat hw_pix_fmt, + AVBufferRef **out_frames_ref); + + + +/** + * @defgroup lavc_parsing Frame parsing + * @{ + */ + +enum AVPictureStructure { + AV_PICTURE_STRUCTURE_UNKNOWN, //< unknown + AV_PICTURE_STRUCTURE_TOP_FIELD, //< coded as top field + AV_PICTURE_STRUCTURE_BOTTOM_FIELD, //< coded as bottom field + AV_PICTURE_STRUCTURE_FRAME, //< coded as frame +}; + +typedef struct AVCodecParserContext { + void *priv_data; + struct AVCodecParser *parser; + int64_t frame_offset; /* offset of the current frame */ + int64_t cur_offset; /* current offset + (incremented by each av_parser_parse()) */ + int64_t next_frame_offset; /* offset of the next frame */ + /* video info */ + int pict_type; /* XXX: Put it back in AVCodecContext. */ + /** + * This field is used for proper frame duration computation in lavf. + * It signals, how much longer the frame duration of the current frame + * is compared to normal frame duration. + * + * frame_duration = (1 + repeat_pict) * time_base + * + * It is used by codecs like H.264 to display telecined material. + */ + int repeat_pict; /* XXX: Put it back in AVCodecContext. */ + int64_t pts; /* pts of the current frame */ + int64_t dts; /* dts of the current frame */ + + /* private data */ + int64_t last_pts; + int64_t last_dts; + int fetch_timestamp; + +#define AV_PARSER_PTS_NB 4 + int cur_frame_start_index; + int64_t cur_frame_offset[AV_PARSER_PTS_NB]; + int64_t cur_frame_pts[AV_PARSER_PTS_NB]; + int64_t cur_frame_dts[AV_PARSER_PTS_NB]; + + int flags; +#define PARSER_FLAG_COMPLETE_FRAMES 0x0001 +#define PARSER_FLAG_ONCE 0x0002 +/// Set if the parser has a valid file offset +#define PARSER_FLAG_FETCHED_OFFSET 0x0004 +#define PARSER_FLAG_USE_CODEC_TS 0x1000 + + int64_t offset; ///< byte offset from starting packet start + int64_t cur_frame_end[AV_PARSER_PTS_NB]; + + /** + * Set by parser to 1 for key frames and 0 for non-key frames. + * It is initialized to -1, so if the parser doesn't set this flag, + * old-style fallback using AV_PICTURE_TYPE_I picture type as key frames + * will be used. + */ + int key_frame; + +#if FF_API_CONVERGENCE_DURATION + /** + * @deprecated unused + */ + attribute_deprecated + int64_t convergence_duration; +#endif + + // Timestamp generation support: + /** + * Synchronization point for start of timestamp generation. + * + * Set to >0 for sync point, 0 for no sync point and <0 for undefined + * (default). + * + * For example, this corresponds to presence of H.264 buffering period + * SEI message. + */ + int dts_sync_point; + + /** + * Offset of the current timestamp against last timestamp sync point in + * units of AVCodecContext.time_base. + * + * Set to INT_MIN when dts_sync_point unused. Otherwise, it must + * contain a valid timestamp offset. + * + * Note that the timestamp of sync point has usually a nonzero + * dts_ref_dts_delta, which refers to the previous sync point. Offset of + * the next frame after timestamp sync point will be usually 1. + * + * For example, this corresponds to H.264 cpb_removal_delay. + */ + int dts_ref_dts_delta; + + /** + * Presentation delay of current frame in units of AVCodecContext.time_base. + * + * Set to INT_MIN when dts_sync_point unused. Otherwise, it must + * contain valid non-negative timestamp delta (presentation time of a frame + * must not lie in the past). + * + * This delay represents the difference between decoding and presentation + * time of the frame. + * + * For example, this corresponds to H.264 dpb_output_delay. + */ + int pts_dts_delta; + + /** + * Position of the packet in file. + * + * Analogous to cur_frame_pts/dts + */ + int64_t cur_frame_pos[AV_PARSER_PTS_NB]; + + /** + * Byte position of currently parsed frame in stream. + */ + int64_t pos; + + /** + * Previous frame byte position. + */ + int64_t last_pos; + + /** + * Duration of the current frame. + * For audio, this is in units of 1 / AVCodecContext.sample_rate. + * For all other types, this is in units of AVCodecContext.time_base. + */ + int duration; + + enum AVFieldOrder field_order; + + /** + * Indicate whether a picture is coded as a frame, top field or bottom field. + * + * For example, H.264 field_pic_flag equal to 0 corresponds to + * AV_PICTURE_STRUCTURE_FRAME. An H.264 picture with field_pic_flag + * equal to 1 and bottom_field_flag equal to 0 corresponds to + * AV_PICTURE_STRUCTURE_TOP_FIELD. + */ + enum AVPictureStructure picture_structure; + + /** + * Picture number incremented in presentation or output order. + * This field may be reinitialized at the first picture of a new sequence. + * + * For example, this corresponds to H.264 PicOrderCnt. + */ + int output_picture_number; + + /** + * Dimensions of the decoded video intended for presentation. + */ + int width; + int height; + + /** + * Dimensions of the coded video. + */ + int coded_width; + int coded_height; + + /** + * The format of the coded data, corresponds to enum AVPixelFormat for video + * and for enum AVSampleFormat for audio. + * + * Note that a decoder can have considerable freedom in how exactly it + * decodes the data, so the format reported here might be different from the + * one returned by a decoder. + */ + int format; +} AVCodecParserContext; + +typedef struct AVCodecParser { + int codec_ids[5]; /* several codec IDs are permitted */ + int priv_data_size; + int (*parser_init)(AVCodecParserContext *s); + /* This callback never returns an error, a negative value means that + * the frame start was in a previous packet. */ + int (*parser_parse)(AVCodecParserContext *s, + AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size); + void (*parser_close)(AVCodecParserContext *s); + int (*split)(AVCodecContext *avctx, const uint8_t *buf, int buf_size); + struct AVCodecParser *next; +} AVCodecParser; + +/** + * Iterate over all registered codec parsers. + * + * @param opaque a pointer where libavcodec will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered codec parser or NULL when the iteration is + * finished + */ +const AVCodecParser *av_parser_iterate(void **opaque); + +attribute_deprecated +AVCodecParser *av_parser_next(const AVCodecParser *c); + +attribute_deprecated +void av_register_codec_parser(AVCodecParser *parser); +AVCodecParserContext *av_parser_init(int codec_id); + +/** + * Parse a packet. + * + * @param s parser context. + * @param avctx codec context. + * @param poutbuf set to pointer to parsed buffer or NULL if not yet finished. + * @param poutbuf_size set to size of parsed buffer or zero if not yet finished. + * @param buf input buffer. + * @param buf_size buffer size in bytes without the padding. I.e. the full buffer + size is assumed to be buf_size + AV_INPUT_BUFFER_PADDING_SIZE. + To signal EOF, this should be 0 (so that the last frame + can be output). + * @param pts input presentation timestamp. + * @param dts input decoding timestamp. + * @param pos input byte position in stream. + * @return the number of bytes of the input bitstream used. + * + * Example: + * @code + * while(in_len){ + * len = av_parser_parse2(myparser, AVCodecContext, &data, &size, + * in_data, in_len, + * pts, dts, pos); + * in_data += len; + * in_len -= len; + * + * if(size) + * decode_frame(data, size); + * } + * @endcode + */ +int av_parser_parse2(AVCodecParserContext *s, + AVCodecContext *avctx, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, + int64_t pts, int64_t dts, + int64_t pos); + +/** + * @return 0 if the output buffer is a subset of the input, 1 if it is allocated and must be freed + * @deprecated use AVBitStreamFilter + */ +int av_parser_change(AVCodecParserContext *s, + AVCodecContext *avctx, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, int keyframe); +void av_parser_close(AVCodecParserContext *s); + +/** + * @} + * @} + */ + +/** + * @addtogroup lavc_encoding + * @{ + */ + +/** + * Find a registered encoder with a matching codec ID. + * + * @param id AVCodecID of the requested encoder + * @return An encoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_encoder(enum AVCodecID id); + +/** + * Find a registered encoder with the specified name. + * + * @param name name of the requested encoder + * @return An encoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_encoder_by_name(const char *name); + +/** + * Encode a frame of audio. + * + * Takes input samples from frame and writes the next output packet, if + * available, to avpkt. The output packet does not necessarily contain data for + * the most recent frame, as encoders can delay, split, and combine input frames + * internally as needed. + * + * @param avctx codec context + * @param avpkt output AVPacket. + * The user can supply an output buffer by setting + * avpkt->data and avpkt->size prior to calling the + * function, but if the size of the user-provided data is not + * large enough, encoding will fail. If avpkt->data and + * avpkt->size are set, avpkt->destruct must also be set. All + * other AVPacket fields will be reset by the encoder using + * av_init_packet(). If avpkt->data is NULL, the encoder will + * allocate it. The encoder will set avpkt->size to the size + * of the output packet. + * + * If this function fails or produces no output, avpkt will be + * freed using av_packet_unref(). + * @param[in] frame AVFrame containing the raw audio data to be encoded. + * May be NULL when flushing an encoder that has the + * AV_CODEC_CAP_DELAY capability set. + * If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame + * can have any number of samples. + * If it is not set, frame->nb_samples must be equal to + * avctx->frame_size for all frames except the last. + * The final frame may be smaller than avctx->frame_size. + * @param[out] got_packet_ptr This field is set to 1 by libavcodec if the + * output packet is non-empty, and to 0 if it is + * empty. If the function returns an error, the + * packet can be assumed to be invalid, and the + * value of got_packet_ptr is undefined and should + * not be used. + * @return 0 on success, negative error code on failure + * + * @deprecated use avcodec_send_frame()/avcodec_receive_packet() instead + */ +attribute_deprecated +int avcodec_encode_audio2(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet_ptr); + +/** + * Encode a frame of video. + * + * Takes input raw video data from frame and writes the next output packet, if + * available, to avpkt. The output packet does not necessarily contain data for + * the most recent frame, as encoders can delay and reorder input frames + * internally as needed. + * + * @param avctx codec context + * @param avpkt output AVPacket. + * The user can supply an output buffer by setting + * avpkt->data and avpkt->size prior to calling the + * function, but if the size of the user-provided data is not + * large enough, encoding will fail. All other AVPacket fields + * will be reset by the encoder using av_init_packet(). If + * avpkt->data is NULL, the encoder will allocate it. + * The encoder will set avpkt->size to the size of the + * output packet. The returned data (if any) belongs to the + * caller, he is responsible for freeing it. + * + * If this function fails or produces no output, avpkt will be + * freed using av_packet_unref(). + * @param[in] frame AVFrame containing the raw video data to be encoded. + * May be NULL when flushing an encoder that has the + * AV_CODEC_CAP_DELAY capability set. + * @param[out] got_packet_ptr This field is set to 1 by libavcodec if the + * output packet is non-empty, and to 0 if it is + * empty. If the function returns an error, the + * packet can be assumed to be invalid, and the + * value of got_packet_ptr is undefined and should + * not be used. + * @return 0 on success, negative error code on failure + * + * @deprecated use avcodec_send_frame()/avcodec_receive_packet() instead + */ +attribute_deprecated +int avcodec_encode_video2(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet_ptr); + +int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, + const AVSubtitle *sub); + + +/** + * @} + */ + +#if FF_API_AVPICTURE +/** + * @addtogroup lavc_picture + * @{ + */ + +/** + * @deprecated unused + */ +attribute_deprecated +int avpicture_alloc(AVPicture *picture, enum AVPixelFormat pix_fmt, int width, int height); + +/** + * @deprecated unused + */ +attribute_deprecated +void avpicture_free(AVPicture *picture); + +/** + * @deprecated use av_image_fill_arrays() instead. + */ +attribute_deprecated +int avpicture_fill(AVPicture *picture, const uint8_t *ptr, + enum AVPixelFormat pix_fmt, int width, int height); + +/** + * @deprecated use av_image_copy_to_buffer() instead. + */ +attribute_deprecated +int avpicture_layout(const AVPicture *src, enum AVPixelFormat pix_fmt, + int width, int height, + unsigned char *dest, int dest_size); + +/** + * @deprecated use av_image_get_buffer_size() instead. + */ +attribute_deprecated +int avpicture_get_size(enum AVPixelFormat pix_fmt, int width, int height); + +/** + * @deprecated av_image_copy() instead. + */ +attribute_deprecated +void av_picture_copy(AVPicture *dst, const AVPicture *src, + enum AVPixelFormat pix_fmt, int width, int height); + +/** + * @deprecated unused + */ +attribute_deprecated +int av_picture_crop(AVPicture *dst, const AVPicture *src, + enum AVPixelFormat pix_fmt, int top_band, int left_band); + +/** + * @deprecated unused + */ +attribute_deprecated +int av_picture_pad(AVPicture *dst, const AVPicture *src, int height, int width, enum AVPixelFormat pix_fmt, + int padtop, int padbottom, int padleft, int padright, int *color); + +/** + * @} + */ +#endif + +/** + * @defgroup lavc_misc Utility functions + * @ingroup libavc + * + * Miscellaneous utility functions related to both encoding and decoding + * (or neither). + * @{ + */ + +/** + * @defgroup lavc_misc_pixfmt Pixel formats + * + * Functions for working with pixel formats. + * @{ + */ + +#if FF_API_GETCHROMA +/** + * @deprecated Use av_pix_fmt_get_chroma_sub_sample + */ + +attribute_deprecated +void avcodec_get_chroma_sub_sample(enum AVPixelFormat pix_fmt, int *h_shift, int *v_shift); +#endif + +/** + * Return a value representing the fourCC code associated to the + * pixel format pix_fmt, or 0 if no associated fourCC code can be + * found. + */ +unsigned int avcodec_pix_fmt_to_codec_tag(enum AVPixelFormat pix_fmt); + +/** + * @deprecated see av_get_pix_fmt_loss() + */ +int avcodec_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, enum AVPixelFormat src_pix_fmt, + int has_alpha); + +/** + * Find the best pixel format to convert to given a certain source pixel + * format. When converting from one pixel format to another, information loss + * may occur. For example, when converting from RGB24 to GRAY, the color + * information will be lost. Similarly, other losses occur when converting from + * some formats to other formats. avcodec_find_best_pix_fmt_of_2() searches which of + * the given pixel formats should be used to suffer the least amount of loss. + * The pixel formats from which it chooses one, are determined by the + * pix_fmt_list parameter. + * + * + * @param[in] pix_fmt_list AV_PIX_FMT_NONE terminated array of pixel formats to choose from + * @param[in] src_pix_fmt source pixel format + * @param[in] has_alpha Whether the source pixel format alpha channel is used. + * @param[out] loss_ptr Combination of flags informing you what kind of losses will occur. + * @return The best pixel format to convert to or -1 if none was found. + */ +enum AVPixelFormat avcodec_find_best_pix_fmt_of_list(const enum AVPixelFormat *pix_fmt_list, + enum AVPixelFormat src_pix_fmt, + int has_alpha, int *loss_ptr); + +/** + * @deprecated see av_find_best_pix_fmt_of_2() + */ +enum AVPixelFormat avcodec_find_best_pix_fmt_of_2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, + enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr); + +attribute_deprecated +enum AVPixelFormat avcodec_find_best_pix_fmt2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, + enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr); + +enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *s, const enum AVPixelFormat * fmt); + +/** + * @} + */ + +#if FF_API_TAG_STRING +/** + * Put a string representing the codec tag codec_tag in buf. + * + * @param buf buffer to place codec tag in + * @param buf_size size in bytes of buf + * @param codec_tag codec tag to assign + * @return the length of the string that would have been generated if + * enough space had been available, excluding the trailing null + * + * @deprecated see av_fourcc_make_string() and av_fourcc2str(). + */ +attribute_deprecated +size_t av_get_codec_tag_string(char *buf, size_t buf_size, unsigned int codec_tag); +#endif + +void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode); + +/** + * Return a name for the specified profile, if available. + * + * @param codec the codec that is searched for the given profile + * @param profile the profile value for which a name is requested + * @return A name for the profile if found, NULL otherwise. + */ +const char *av_get_profile_name(const AVCodec *codec, int profile); + +/** + * Return a name for the specified profile, if available. + * + * @param codec_id the ID of the codec to which the requested profile belongs + * @param profile the profile value for which a name is requested + * @return A name for the profile if found, NULL otherwise. + * + * @note unlike av_get_profile_name(), which searches a list of profiles + * supported by a specific decoder or encoder implementation, this + * function searches the list of profiles from the AVCodecDescriptor + */ +const char *avcodec_profile_name(enum AVCodecID codec_id, int profile); + +int avcodec_default_execute(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2),void *arg, int *ret, int count, int size); +int avcodec_default_execute2(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2, int, int),void *arg, int *ret, int count); +//FIXME func typedef + +/** + * Fill AVFrame audio data and linesize pointers. + * + * The buffer buf must be a preallocated buffer with a size big enough + * to contain the specified samples amount. The filled AVFrame data + * pointers will point to this buffer. + * + * AVFrame extended_data channel pointers are allocated if necessary for + * planar audio. + * + * @param frame the AVFrame + * frame->nb_samples must be set prior to calling the + * function. This function fills in frame->data, + * frame->extended_data, frame->linesize[0]. + * @param nb_channels channel count + * @param sample_fmt sample format + * @param buf buffer to use for frame data + * @param buf_size size of buffer + * @param align plane size sample alignment (0 = default) + * @return >=0 on success, negative error code on failure + * @todo return the size in bytes required to store the samples in + * case of success, at the next libavutil bump + */ +int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, + enum AVSampleFormat sample_fmt, const uint8_t *buf, + int buf_size, int align); + +/** + * Reset the internal decoder state / flush internal buffers. Should be called + * e.g. when seeking or when switching to a different stream. + * + * @note when refcounted frames are not used (i.e. avctx->refcounted_frames is 0), + * this invalidates the frames previously returned from the decoder. When + * refcounted frames are used, the decoder just releases any references it might + * keep internally, but the caller's reference remains valid. + */ +void avcodec_flush_buffers(AVCodecContext *avctx); + +/** + * Return codec bits per sample. + * + * @param[in] codec_id the codec + * @return Number of bits per sample or zero if unknown for the given codec. + */ +int av_get_bits_per_sample(enum AVCodecID codec_id); + +/** + * Return the PCM codec associated with a sample format. + * @param be endianness, 0 for little, 1 for big, + * -1 (or anything else) for native + * @return AV_CODEC_ID_PCM_* or AV_CODEC_ID_NONE + */ +enum AVCodecID av_get_pcm_codec(enum AVSampleFormat fmt, int be); + +/** + * Return codec bits per sample. + * Only return non-zero if the bits per sample is exactly correct, not an + * approximation. + * + * @param[in] codec_id the codec + * @return Number of bits per sample or zero if unknown for the given codec. + */ +int av_get_exact_bits_per_sample(enum AVCodecID codec_id); + +/** + * Return audio frame duration. + * + * @param avctx codec context + * @param frame_bytes size of the frame, or 0 if unknown + * @return frame duration, in samples, if known. 0 if not able to + * determine. + */ +int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes); + +/** + * This function is the same as av_get_audio_frame_duration(), except it works + * with AVCodecParameters instead of an AVCodecContext. + */ +int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes); + +#if FF_API_OLD_BSF +typedef struct AVBitStreamFilterContext { + void *priv_data; + const struct AVBitStreamFilter *filter; + AVCodecParserContext *parser; + struct AVBitStreamFilterContext *next; + /** + * Internal default arguments, used if NULL is passed to av_bitstream_filter_filter(). + * Not for access by library users. + */ + char *args; +} AVBitStreamFilterContext; +#endif + +typedef struct AVBSFInternal AVBSFInternal; + +/** + * The bitstream filter state. + * + * This struct must be allocated with av_bsf_alloc() and freed with + * av_bsf_free(). + * + * The fields in the struct will only be changed (by the caller or by the + * filter) as described in their documentation, and are to be considered + * immutable otherwise. + */ +typedef struct AVBSFContext { + /** + * A class for logging and AVOptions + */ + const AVClass *av_class; + + /** + * The bitstream filter this context is an instance of. + */ + const struct AVBitStreamFilter *filter; + + /** + * Opaque libavcodec internal data. Must not be touched by the caller in any + * way. + */ + AVBSFInternal *internal; + + /** + * Opaque filter-specific private data. If filter->priv_class is non-NULL, + * this is an AVOptions-enabled struct. + */ + void *priv_data; + + /** + * Parameters of the input stream. This field is allocated in + * av_bsf_alloc(), it needs to be filled by the caller before + * av_bsf_init(). + */ + AVCodecParameters *par_in; + + /** + * Parameters of the output stream. This field is allocated in + * av_bsf_alloc(), it is set by the filter in av_bsf_init(). + */ + AVCodecParameters *par_out; + + /** + * The timebase used for the timestamps of the input packets. Set by the + * caller before av_bsf_init(). + */ + AVRational time_base_in; + + /** + * The timebase used for the timestamps of the output packets. Set by the + * filter in av_bsf_init(). + */ + AVRational time_base_out; +} AVBSFContext; + +typedef struct AVBitStreamFilter { + const char *name; + + /** + * A list of codec ids supported by the filter, terminated by + * AV_CODEC_ID_NONE. + * May be NULL, in that case the bitstream filter works with any codec id. + */ + const enum AVCodecID *codec_ids; + + /** + * A class for the private data, used to declare bitstream filter private + * AVOptions. This field is NULL for bitstream filters that do not declare + * any options. + * + * If this field is non-NULL, the first member of the filter private data + * must be a pointer to AVClass, which will be set by libavcodec generic + * code to this class. + */ + const AVClass *priv_class; + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavcodec and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + + int priv_data_size; + int (*init)(AVBSFContext *ctx); + int (*filter)(AVBSFContext *ctx, AVPacket *pkt); + void (*close)(AVBSFContext *ctx); + void (*flush)(AVBSFContext *ctx); +} AVBitStreamFilter; + +#if FF_API_OLD_BSF +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use the new bitstream filtering API (using AVBSFContext). + */ +attribute_deprecated +void av_register_bitstream_filter(AVBitStreamFilter *bsf); +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use av_bsf_get_by_name(), av_bsf_alloc(), and av_bsf_init() + * from the new bitstream filtering API (using AVBSFContext). + */ +attribute_deprecated +AVBitStreamFilterContext *av_bitstream_filter_init(const char *name); +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use av_bsf_send_packet() and av_bsf_receive_packet() from the + * new bitstream filtering API (using AVBSFContext). + */ +attribute_deprecated +int av_bitstream_filter_filter(AVBitStreamFilterContext *bsfc, + AVCodecContext *avctx, const char *args, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, int keyframe); +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use av_bsf_free() from the new bitstream filtering API (using + * AVBSFContext). + */ +attribute_deprecated +void av_bitstream_filter_close(AVBitStreamFilterContext *bsf); +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use av_bsf_iterate() from the new bitstream filtering API (using + * AVBSFContext). + */ +attribute_deprecated +const AVBitStreamFilter *av_bitstream_filter_next(const AVBitStreamFilter *f); +#endif + +/** + * @return a bitstream filter with the specified name or NULL if no such + * bitstream filter exists. + */ +const AVBitStreamFilter *av_bsf_get_by_name(const char *name); + +/** + * Iterate over all registered bitstream filters. + * + * @param opaque a pointer where libavcodec will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered bitstream filter or NULL when the iteration is + * finished + */ +const AVBitStreamFilter *av_bsf_iterate(void **opaque); +#if FF_API_NEXT +attribute_deprecated +const AVBitStreamFilter *av_bsf_next(void **opaque); +#endif + +/** + * Allocate a context for a given bitstream filter. The caller must fill in the + * context parameters as described in the documentation and then call + * av_bsf_init() before sending any data to the filter. + * + * @param filter the filter for which to allocate an instance. + * @param ctx a pointer into which the pointer to the newly-allocated context + * will be written. It must be freed with av_bsf_free() after the + * filtering is done. + * + * @return 0 on success, a negative AVERROR code on failure + */ +int av_bsf_alloc(const AVBitStreamFilter *filter, AVBSFContext **ctx); + +/** + * Prepare the filter for use, after all the parameters and options have been + * set. + */ +int av_bsf_init(AVBSFContext *ctx); + +/** + * Submit a packet for filtering. + * + * After sending each packet, the filter must be completely drained by calling + * av_bsf_receive_packet() repeatedly until it returns AVERROR(EAGAIN) or + * AVERROR_EOF. + * + * @param pkt the packet to filter. The bitstream filter will take ownership of + * the packet and reset the contents of pkt. pkt is not touched if an error occurs. + * This parameter may be NULL, which signals the end of the stream (i.e. no more + * packets will be sent). That will cause the filter to output any packets it + * may have buffered internally. + * + * @return 0 on success, a negative AVERROR on error. + */ +int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt); + +/** + * Retrieve a filtered packet. + * + * @param[out] pkt this struct will be filled with the contents of the filtered + * packet. It is owned by the caller and must be freed using + * av_packet_unref() when it is no longer needed. + * This parameter should be "clean" (i.e. freshly allocated + * with av_packet_alloc() or unreffed with av_packet_unref()) + * when this function is called. If this function returns + * successfully, the contents of pkt will be completely + * overwritten by the returned data. On failure, pkt is not + * touched. + * + * @return 0 on success. AVERROR(EAGAIN) if more packets need to be sent to the + * filter (using av_bsf_send_packet()) to get more output. AVERROR_EOF if there + * will be no further output from the filter. Another negative AVERROR value if + * an error occurs. + * + * @note one input packet may result in several output packets, so after sending + * a packet with av_bsf_send_packet(), this function needs to be called + * repeatedly until it stops returning 0. It is also possible for a filter to + * output fewer packets than were sent to it, so this function may return + * AVERROR(EAGAIN) immediately after a successful av_bsf_send_packet() call. + */ +int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt); + +/** + * Reset the internal bitstream filter state / flush internal buffers. + */ +void av_bsf_flush(AVBSFContext *ctx); + +/** + * Free a bitstream filter context and everything associated with it; write NULL + * into the supplied pointer. + */ +void av_bsf_free(AVBSFContext **ctx); + +/** + * Get the AVClass for AVBSFContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *av_bsf_get_class(void); + +/** + * Structure for chain/list of bitstream filters. + * Empty list can be allocated by av_bsf_list_alloc(). + */ +typedef struct AVBSFList AVBSFList; + +/** + * Allocate empty list of bitstream filters. + * The list must be later freed by av_bsf_list_free() + * or finalized by av_bsf_list_finalize(). + * + * @return Pointer to @ref AVBSFList on success, NULL in case of failure + */ +AVBSFList *av_bsf_list_alloc(void); + +/** + * Free list of bitstream filters. + * + * @param lst Pointer to pointer returned by av_bsf_list_alloc() + */ +void av_bsf_list_free(AVBSFList **lst); + +/** + * Append bitstream filter to the list of bitstream filters. + * + * @param lst List to append to + * @param bsf Filter context to be appended + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_append(AVBSFList *lst, AVBSFContext *bsf); + +/** + * Construct new bitstream filter context given it's name and options + * and append it to the list of bitstream filters. + * + * @param lst List to append to + * @param bsf_name Name of the bitstream filter + * @param options Options for the bitstream filter, can be set to NULL + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_append2(AVBSFList *lst, const char * bsf_name, AVDictionary **options); +/** + * Finalize list of bitstream filters. + * + * This function will transform @ref AVBSFList to single @ref AVBSFContext, + * so the whole chain of bitstream filters can be treated as single filter + * freshly allocated by av_bsf_alloc(). + * If the call is successful, @ref AVBSFList structure is freed and lst + * will be set to NULL. In case of failure, caller is responsible for + * freeing the structure by av_bsf_list_free() + * + * @param lst Filter list structure to be transformed + * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_finalize(AVBSFList **lst, AVBSFContext **bsf); + +/** + * Parse string describing list of bitstream filters and create single + * @ref AVBSFContext describing the whole chain of bitstream filters. + * Resulting @ref AVBSFContext can be treated as any other @ref AVBSFContext freshly + * allocated by av_bsf_alloc(). + * + * @param str String describing chain of bitstream filters in format + * `bsf1[=opt1=val1:opt2=val2][,bsf2]` + * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_parse_str(const char *str, AVBSFContext **bsf); + +/** + * Get null/pass-through bitstream filter. + * + * @param[out] bsf Pointer to be set to new instance of pass-through bitstream filter + * + * @return + */ +int av_bsf_get_null_filter(AVBSFContext **bsf); + +/* memory */ + +/** + * Same behaviour av_fast_malloc but the buffer has additional + * AV_INPUT_BUFFER_PADDING_SIZE at the end which will always be 0. + * + * In addition the whole buffer will initially and after resizes + * be 0-initialized so that no uninitialized data will ever appear. + */ +void av_fast_padded_malloc(void *ptr, unsigned int *size, size_t min_size); + +/** + * Same behaviour av_fast_padded_malloc except that buffer will always + * be 0-initialized after call. + */ +void av_fast_padded_mallocz(void *ptr, unsigned int *size, size_t min_size); + +/** + * Encode extradata length to a buffer. Used by xiph codecs. + * + * @param s buffer to write to; must be at least (v/255+1) bytes long + * @param v size of extradata in bytes + * @return number of bytes written to the buffer. + */ +unsigned int av_xiphlacing(unsigned char *s, unsigned int v); + +#if FF_API_USER_VISIBLE_AVHWACCEL +/** + * Register the hardware accelerator hwaccel. + * + * @deprecated This function doesn't do anything. + */ +attribute_deprecated +void av_register_hwaccel(AVHWAccel *hwaccel); + +/** + * If hwaccel is NULL, returns the first registered hardware accelerator, + * if hwaccel is non-NULL, returns the next registered hardware accelerator + * after hwaccel, or NULL if hwaccel is the last one. + * + * @deprecated AVHWaccel structures contain no user-serviceable parts, so + * this function should not be used. + */ +attribute_deprecated +AVHWAccel *av_hwaccel_next(const AVHWAccel *hwaccel); +#endif + +#if FF_API_LOCKMGR +/** + * Lock operation used by lockmgr + * + * @deprecated Deprecated together with av_lockmgr_register(). + */ +enum AVLockOp { + AV_LOCK_CREATE, ///< Create a mutex + AV_LOCK_OBTAIN, ///< Lock the mutex + AV_LOCK_RELEASE, ///< Unlock the mutex + AV_LOCK_DESTROY, ///< Free mutex resources +}; + +/** + * Register a user provided lock manager supporting the operations + * specified by AVLockOp. The "mutex" argument to the function points + * to a (void *) where the lockmgr should store/get a pointer to a user + * allocated mutex. It is NULL upon AV_LOCK_CREATE and equal to the + * value left by the last call for all other ops. If the lock manager is + * unable to perform the op then it should leave the mutex in the same + * state as when it was called and return a non-zero value. However, + * when called with AV_LOCK_DESTROY the mutex will always be assumed to + * have been successfully destroyed. If av_lockmgr_register succeeds + * it will return a non-negative value, if it fails it will return a + * negative value and destroy all mutex and unregister all callbacks. + * av_lockmgr_register is not thread-safe, it must be called from a + * single thread before any calls which make use of locking are used. + * + * @param cb User defined callback. av_lockmgr_register invokes calls + * to this callback and the previously registered callback. + * The callback will be used to create more than one mutex + * each of which must be backed by its own underlying locking + * mechanism (i.e. do not use a single static object to + * implement your lock manager). If cb is set to NULL the + * lockmgr will be unregistered. + * + * @deprecated This function does nothing, and always returns 0. Be sure to + * build with thread support to get basic thread safety. + */ +attribute_deprecated +int av_lockmgr_register(int (*cb)(void **mutex, enum AVLockOp op)); +#endif + +/** + * Get the type of the given codec. + */ +enum AVMediaType avcodec_get_type(enum AVCodecID codec_id); + +/** + * Get the name of a codec. + * @return a static string identifying the codec; never NULL + */ +const char *avcodec_get_name(enum AVCodecID id); + +/** + * @return a positive value if s is open (i.e. avcodec_open2() was called on it + * with no corresponding avcodec_close()), 0 otherwise. + */ +int avcodec_is_open(AVCodecContext *s); + +/** + * @return a non-zero number if codec is an encoder, zero otherwise + */ +int av_codec_is_encoder(const AVCodec *codec); + +/** + * @return a non-zero number if codec is a decoder, zero otherwise + */ +int av_codec_is_decoder(const AVCodec *codec); + +/** + * @return descriptor for given codec ID or NULL if no descriptor exists. + */ +const AVCodecDescriptor *avcodec_descriptor_get(enum AVCodecID id); + +/** + * Iterate over all codec descriptors known to libavcodec. + * + * @param prev previous descriptor. NULL to get the first descriptor. + * + * @return next descriptor or NULL after the last descriptor + */ +const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev); + +/** + * @return codec descriptor with the given name or NULL if no such descriptor + * exists. + */ +const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name); + +/** + * Allocate a CPB properties structure and initialize its fields to default + * values. + * + * @param size if non-NULL, the size of the allocated struct will be written + * here. This is useful for embedding it in side data. + * + * @return the newly allocated struct or NULL on failure + */ +AVCPBProperties *av_cpb_properties_alloc(size_t *size); + +/** + * @} + */ + +#endif /* AVCODEC_AVCODEC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avdct.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avdct.h new file mode 100644 index 0000000..272422e --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avdct.h
@@ -0,0 +1,84 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_AVDCT_H +#define AVCODEC_AVDCT_H + +#include "libavutil/opt.h" + +/** + * AVDCT context. + * @note function pointers can be NULL if the specific features have been + * disabled at build time. + */ +typedef struct AVDCT { + const AVClass *av_class; + + void (*idct)(int16_t *block /* align 16 */); + + /** + * IDCT input permutation. + * Several optimized IDCTs need a permutated input (relative to the + * normal order of the reference IDCT). + * This permutation must be performed before the idct_put/add. + * Note, normally this can be merged with the zigzag/alternate scan<br> + * An example to avoid confusion: + * - (->decode coeffs -> zigzag reorder -> dequant -> reference IDCT -> ...) + * - (x -> reference DCT -> reference IDCT -> x) + * - (x -> reference DCT -> simple_mmx_perm = idct_permutation + * -> simple_idct_mmx -> x) + * - (-> decode coeffs -> zigzag reorder -> simple_mmx_perm -> dequant + * -> simple_idct_mmx -> ...) + */ + uint8_t idct_permutation[64]; + + void (*fdct)(int16_t *block /* align 16 */); + + + /** + * DCT algorithm. + * must use AVOptions to set this field. + */ + int dct_algo; + + /** + * IDCT algorithm. + * must use AVOptions to set this field. + */ + int idct_algo; + + void (*get_pixels)(int16_t *block /* align 16 */, + const uint8_t *pixels /* align 8 */, + ptrdiff_t line_size); + + int bits_per_sample; +} AVDCT; + +/** + * Allocates a AVDCT context. + * This needs to be initialized with avcodec_dct_init() after optionally + * configuring it with AVOptions. + * + * To free it use av_free() + */ +AVDCT *avcodec_dct_alloc(void); +int avcodec_dct_init(AVDCT *); + +const AVClass *avcodec_dct_get_class(void); + +#endif /* AVCODEC_AVDCT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avfft.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avfft.h new file mode 100644 index 0000000..0c0f9b8 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avfft.h
@@ -0,0 +1,118 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_AVFFT_H +#define AVCODEC_AVFFT_H + +/** + * @file + * @ingroup lavc_fft + * FFT functions + */ + +/** + * @defgroup lavc_fft FFT functions + * @ingroup lavc_misc + * + * @{ + */ + +typedef float FFTSample; + +typedef struct FFTComplex { + FFTSample re, im; +} FFTComplex; + +typedef struct FFTContext FFTContext; + +/** + * Set up a complex FFT. + * @param nbits log2 of the length of the input array + * @param inverse if 0 perform the forward transform, if 1 perform the inverse + */ +FFTContext *av_fft_init(int nbits, int inverse); + +/** + * Do the permutation needed BEFORE calling ff_fft_calc(). + */ +void av_fft_permute(FFTContext *s, FFTComplex *z); + +/** + * Do a complex FFT with the parameters defined in av_fft_init(). The + * input data must be permuted before. No 1.0/sqrt(n) normalization is done. + */ +void av_fft_calc(FFTContext *s, FFTComplex *z); + +void av_fft_end(FFTContext *s); + +FFTContext *av_mdct_init(int nbits, int inverse, double scale); +void av_imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); +void av_imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input); +void av_mdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); +void av_mdct_end(FFTContext *s); + +/* Real Discrete Fourier Transform */ + +enum RDFTransformType { + DFT_R2C, + IDFT_C2R, + IDFT_R2C, + DFT_C2R, +}; + +typedef struct RDFTContext RDFTContext; + +/** + * Set up a real FFT. + * @param nbits log2 of the length of the input array + * @param trans the type of transform + */ +RDFTContext *av_rdft_init(int nbits, enum RDFTransformType trans); +void av_rdft_calc(RDFTContext *s, FFTSample *data); +void av_rdft_end(RDFTContext *s); + +/* Discrete Cosine Transform */ + +typedef struct DCTContext DCTContext; + +enum DCTTransformType { + DCT_II = 0, + DCT_III, + DCT_I, + DST_I, +}; + +/** + * Set up DCT. + * + * @param nbits size of the input array: + * (1 << nbits) for DCT-II, DCT-III and DST-I + * (1 << nbits) + 1 for DCT-I + * @param type the type of transform + * + * @note the first element of the input of DST-I is ignored + */ +DCTContext *av_dct_init(int nbits, enum DCTTransformType type); +void av_dct_calc(DCTContext *s, FFTSample *data); +void av_dct_end (DCTContext *s); + +/** + * @} + */ + +#endif /* AVCODEC_AVFFT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/d3d11va.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/d3d11va.h new file mode 100644 index 0000000..6816b6c --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/d3d11va.h
@@ -0,0 +1,112 @@ +/* + * Direct3D11 HW acceleration + * + * copyright (c) 2009 Laurent Aimar + * copyright (c) 2015 Steve Lhomme + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_D3D11VA_H +#define AVCODEC_D3D11VA_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_d3d11va + * Public libavcodec D3D11VA header. + */ + +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0602 +#endif + +#include <stdint.h> +#include <d3d11.h> + +/** + * @defgroup lavc_codec_hwaccel_d3d11va Direct3D11 + * @ingroup lavc_codec_hwaccel + * + * @{ + */ + +#define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for Direct3D11 and old UVD/UVD+ ATI video cards +#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2 ///< Work around for Direct3D11 and old Intel GPUs with ClearVideo interface + +/** + * This structure is used to provides the necessary configurations and data + * to the Direct3D11 FFmpeg HWAccel implementation. + * + * The application must make it available as AVCodecContext.hwaccel_context. + * + * Use av_d3d11va_alloc_context() exclusively to allocate an AVD3D11VAContext. + */ +typedef struct AVD3D11VAContext { + /** + * D3D11 decoder object + */ + ID3D11VideoDecoder *decoder; + + /** + * D3D11 VideoContext + */ + ID3D11VideoContext *video_context; + + /** + * D3D11 configuration used to create the decoder + */ + D3D11_VIDEO_DECODER_CONFIG *cfg; + + /** + * The number of surface in the surface array + */ + unsigned surface_count; + + /** + * The array of Direct3D surfaces used to create the decoder + */ + ID3D11VideoDecoderOutputView **surface; + + /** + * A bit field configuring the workarounds needed for using the decoder + */ + uint64_t workaround; + + /** + * Private to the FFmpeg AVHWAccel implementation + */ + unsigned report_id; + + /** + * Mutex to access video_context + */ + HANDLE context_mutex; +} AVD3D11VAContext; + +/** + * Allocate an AVD3D11VAContext. + * + * @return Newly-allocated AVD3D11VAContext or NULL on failure. + */ +AVD3D11VAContext *av_d3d11va_alloc_context(void); + +/** + * @} + */ + +#endif /* AVCODEC_D3D11VA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dirac.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dirac.h new file mode 100644 index 0000000..e6d9d34 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dirac.h
@@ -0,0 +1,131 @@ +/* + * Copyright (C) 2007 Marco Gerards <marco@gnu.org> + * Copyright (C) 2009 David Conrad + * Copyright (C) 2011 Jordi Ortiz + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_DIRAC_H +#define AVCODEC_DIRAC_H + +/** + * @file + * Interface to Dirac Decoder/Encoder + * @author Marco Gerards <marco@gnu.org> + * @author David Conrad + * @author Jordi Ortiz + */ + +#include "avcodec.h" + +/** + * The spec limits the number of wavelet decompositions to 4 for both + * level 1 (VC-2) and 128 (long-gop default). + * 5 decompositions is the maximum before >16-bit buffers are needed. + * Schroedinger allows this for DD 9,7 and 13,7 wavelets only, limiting + * the others to 4 decompositions (or 3 for the fidelity filter). + * + * We use this instead of MAX_DECOMPOSITIONS to save some memory. + */ +#define MAX_DWT_LEVELS 5 + +/** + * Parse code values: + * + * Dirac Specification -> + * 9.6.1 Table 9.1 + * + * VC-2 Specification -> + * 10.4.1 Table 10.1 + */ + +enum DiracParseCodes { + DIRAC_PCODE_SEQ_HEADER = 0x00, + DIRAC_PCODE_END_SEQ = 0x10, + DIRAC_PCODE_AUX = 0x20, + DIRAC_PCODE_PAD = 0x30, + DIRAC_PCODE_PICTURE_CODED = 0x08, + DIRAC_PCODE_PICTURE_RAW = 0x48, + DIRAC_PCODE_PICTURE_LOW_DEL = 0xC8, + DIRAC_PCODE_PICTURE_HQ = 0xE8, + DIRAC_PCODE_INTER_NOREF_CO1 = 0x0A, + DIRAC_PCODE_INTER_NOREF_CO2 = 0x09, + DIRAC_PCODE_INTER_REF_CO1 = 0x0D, + DIRAC_PCODE_INTER_REF_CO2 = 0x0E, + DIRAC_PCODE_INTRA_REF_CO = 0x0C, + DIRAC_PCODE_INTRA_REF_RAW = 0x4C, + DIRAC_PCODE_INTRA_REF_PICT = 0xCC, + DIRAC_PCODE_MAGIC = 0x42424344, +}; + +typedef struct DiracVersionInfo { + int major; + int minor; +} DiracVersionInfo; + +typedef struct AVDiracSeqHeader { + unsigned width; + unsigned height; + uint8_t chroma_format; ///< 0: 444 1: 422 2: 420 + + uint8_t interlaced; + uint8_t top_field_first; + + uint8_t frame_rate_index; ///< index into dirac_frame_rate[] + uint8_t aspect_ratio_index; ///< index into dirac_aspect_ratio[] + + uint16_t clean_width; + uint16_t clean_height; + uint16_t clean_left_offset; + uint16_t clean_right_offset; + + uint8_t pixel_range_index; ///< index into dirac_pixel_range_presets[] + uint8_t color_spec_index; ///< index into dirac_color_spec_presets[] + + int profile; + int level; + + AVRational framerate; + AVRational sample_aspect_ratio; + + enum AVPixelFormat pix_fmt; + enum AVColorRange color_range; + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_trc; + enum AVColorSpace colorspace; + + DiracVersionInfo version; + int bit_depth; +} AVDiracSeqHeader; + +/** + * Parse a Dirac sequence header. + * + * @param dsh this function will allocate and fill an AVDiracSeqHeader struct + * and write it into this pointer. The caller must free it with + * av_free(). + * @param buf the data buffer + * @param buf_size the size of the data buffer in bytes + * @param log_ctx if non-NULL, this function will log errors here + * @return 0 on success, a negative AVERROR code on failure + */ +int av_dirac_parse_sequence_header(AVDiracSeqHeader **dsh, + const uint8_t *buf, size_t buf_size, + void *log_ctx); + +#endif /* AVCODEC_DIRAC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dv_profile.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dv_profile.h new file mode 100644 index 0000000..9380a66 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dv_profile.h
@@ -0,0 +1,83 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_DV_PROFILE_H +#define AVCODEC_DV_PROFILE_H + +#include <stdint.h> + +#include "libavutil/pixfmt.h" +#include "libavutil/rational.h" +#include "avcodec.h" + +/* minimum number of bytes to read from a DV stream in order to + * determine the profile */ +#define DV_PROFILE_BYTES (6 * 80) /* 6 DIF blocks */ + + +/* + * AVDVProfile is used to express the differences between various + * DV flavors. For now it's primarily used for differentiating + * 525/60 and 625/50, but the plans are to use it for various + * DV specs as well (e.g. SMPTE314M vs. IEC 61834). + */ +typedef struct AVDVProfile { + int dsf; /* value of the dsf in the DV header */ + int video_stype; /* stype for VAUX source pack */ + int frame_size; /* total size of one frame in bytes */ + int difseg_size; /* number of DIF segments per DIF channel */ + int n_difchan; /* number of DIF channels per frame */ + AVRational time_base; /* 1/framerate */ + int ltc_divisor; /* FPS from the LTS standpoint */ + int height; /* picture height in pixels */ + int width; /* picture width in pixels */ + AVRational sar[2]; /* sample aspect ratios for 4:3 and 16:9 */ + enum AVPixelFormat pix_fmt; /* picture pixel format */ + int bpm; /* blocks per macroblock */ + const uint8_t *block_sizes; /* AC block sizes, in bits */ + int audio_stride; /* size of audio_shuffle table */ + int audio_min_samples[3]; /* min amount of audio samples */ + /* for 48kHz, 44.1kHz and 32kHz */ + int audio_samples_dist[5]; /* how many samples are supposed to be */ + /* in each frame in a 5 frames window */ + const uint8_t (*audio_shuffle)[9]; /* PCM shuffling table */ +} AVDVProfile; + +/** + * Get a DV profile for the provided compressed frame. + * + * @param sys the profile used for the previous frame, may be NULL + * @param frame the compressed data buffer + * @param buf_size size of the buffer in bytes + * @return the DV profile for the supplied data or NULL on failure + */ +const AVDVProfile *av_dv_frame_profile(const AVDVProfile *sys, + const uint8_t *frame, unsigned buf_size); + +/** + * Get a DV profile for the provided stream parameters. + */ +const AVDVProfile *av_dv_codec_profile(int width, int height, enum AVPixelFormat pix_fmt); + +/** + * Get a DV profile for the provided stream parameters. + * The frame rate is used as a best-effort parameter. + */ +const AVDVProfile *av_dv_codec_profile2(int width, int height, enum AVPixelFormat pix_fmt, AVRational frame_rate); + +#endif /* AVCODEC_DV_PROFILE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dxva2.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dxva2.h new file mode 100644 index 0000000..22c9399 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dxva2.h
@@ -0,0 +1,93 @@ +/* + * DXVA2 HW acceleration + * + * copyright (c) 2009 Laurent Aimar + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_DXVA2_H +#define AVCODEC_DXVA2_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_dxva2 + * Public libavcodec DXVA2 header. + */ + +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0602 +#endif + +#include <stdint.h> +#include <d3d9.h> +#include <dxva2api.h> + +/** + * @defgroup lavc_codec_hwaccel_dxva2 DXVA2 + * @ingroup lavc_codec_hwaccel + * + * @{ + */ + +#define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for DXVA2 and old UVD/UVD+ ATI video cards +#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2 ///< Work around for DXVA2 and old Intel GPUs with ClearVideo interface + +/** + * This structure is used to provides the necessary configurations and data + * to the DXVA2 FFmpeg HWAccel implementation. + * + * The application must make it available as AVCodecContext.hwaccel_context. + */ +struct dxva_context { + /** + * DXVA2 decoder object + */ + IDirectXVideoDecoder *decoder; + + /** + * DXVA2 configuration used to create the decoder + */ + const DXVA2_ConfigPictureDecode *cfg; + + /** + * The number of surface in the surface array + */ + unsigned surface_count; + + /** + * The array of Direct3D surfaces used to create the decoder + */ + LPDIRECT3DSURFACE9 *surface; + + /** + * A bit field configuring the workarounds needed for using the decoder + */ + uint64_t workaround; + + /** + * Private to the FFmpeg AVHWAccel implementation + */ + unsigned report_id; +}; + +/** + * @} + */ + +#endif /* AVCODEC_DXVA2_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/jni.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/jni.h new file mode 100644 index 0000000..dd99e92 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/jni.h
@@ -0,0 +1,46 @@ +/* + * JNI public API functions + * + * Copyright (c) 2015-2016 Matthieu Bouron <matthieu.bouron stupeflix.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_JNI_H +#define AVCODEC_JNI_H + +/* + * Manually set a Java virtual machine which will be used to retrieve the JNI + * environment. Once a Java VM is set it cannot be changed afterwards, meaning + * you can call multiple times av_jni_set_java_vm with the same Java VM pointer + * however it will error out if you try to set a different Java VM. + * + * @param vm Java virtual machine + * @param log_ctx context used for logging, can be NULL + * @return 0 on success, < 0 otherwise + */ +int av_jni_set_java_vm(void *vm, void *log_ctx); + +/* + * Get the Java virtual machine which has been set with av_jni_set_java_vm. + * + * @param vm Java virtual machine + * @return a pointer to the Java virtual machine + */ +void *av_jni_get_java_vm(void *log_ctx); + +#endif /* AVCODEC_JNI_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/mediacodec.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/mediacodec.h new file mode 100644 index 0000000..4c8545d --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/mediacodec.h
@@ -0,0 +1,101 @@ +/* + * Android MediaCodec public API + * + * Copyright (c) 2016 Matthieu Bouron <matthieu.bouron stupeflix.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_MEDIACODEC_H +#define AVCODEC_MEDIACODEC_H + +#include "libavcodec/avcodec.h" + +/** + * This structure holds a reference to a android/view/Surface object that will + * be used as output by the decoder. + * + */ +typedef struct AVMediaCodecContext { + + /** + * android/view/Surface object reference. + */ + void *surface; + +} AVMediaCodecContext; + +/** + * Allocate and initialize a MediaCodec context. + * + * When decoding with MediaCodec is finished, the caller must free the + * MediaCodec context with av_mediacodec_default_free. + * + * @return a pointer to a newly allocated AVMediaCodecContext on success, NULL otherwise + */ +AVMediaCodecContext *av_mediacodec_alloc_context(void); + +/** + * Convenience function that sets up the MediaCodec context. + * + * @param avctx codec context + * @param ctx MediaCodec context to initialize + * @param surface reference to an android/view/Surface + * @return 0 on success, < 0 otherwise + */ +int av_mediacodec_default_init(AVCodecContext *avctx, AVMediaCodecContext *ctx, void *surface); + +/** + * This function must be called to free the MediaCodec context initialized with + * av_mediacodec_default_init(). + * + * @param avctx codec context + */ +void av_mediacodec_default_free(AVCodecContext *avctx); + +/** + * Opaque structure representing a MediaCodec buffer to render. + */ +typedef struct MediaCodecBuffer AVMediaCodecBuffer; + +/** + * Release a MediaCodec buffer and render it to the surface that is associated + * with the decoder. This function should only be called once on a given + * buffer, once released the underlying buffer returns to the codec, thus + * subsequent calls to this function will have no effect. + * + * @param buffer the buffer to render + * @param render 1 to release and render the buffer to the surface or 0 to + * discard the buffer + * @return 0 on success, < 0 otherwise + */ +int av_mediacodec_release_buffer(AVMediaCodecBuffer *buffer, int render); + +/** + * Release a MediaCodec buffer and render it at the given time to the surface + * that is associated with the decoder. The timestamp must be within one second + * of the current java/lang/System#nanoTime() (which is implemented using + * CLOCK_MONOTONIC on Android). See the Android MediaCodec documentation + * of android/media/MediaCodec#releaseOutputBuffer(int,long) for more details. + * + * @param buffer the buffer to render + * @param time timestamp in nanoseconds of when to render the buffer + * @return 0 on success, < 0 otherwise + */ +int av_mediacodec_render_buffer_at_time(AVMediaCodecBuffer *buffer, int64_t time); + +#endif /* AVCODEC_MEDIACODEC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/qsv.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/qsv.h new file mode 100644 index 0000000..b77158e --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/qsv.h
@@ -0,0 +1,107 @@ +/* + * Intel MediaSDK QSV public API + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_QSV_H +#define AVCODEC_QSV_H + +#include <mfx/mfxvideo.h> + +#include "libavutil/buffer.h" + +/** + * This struct is used for communicating QSV parameters between libavcodec and + * the caller. It is managed by the caller and must be assigned to + * AVCodecContext.hwaccel_context. + * - decoding: hwaccel_context must be set on return from the get_format() + * callback + * - encoding: hwaccel_context must be set before avcodec_open2() + */ +typedef struct AVQSVContext { + /** + * If non-NULL, the session to use for encoding or decoding. + * Otherwise, libavcodec will try to create an internal session. + */ + mfxSession session; + + /** + * The IO pattern to use. + */ + int iopattern; + + /** + * Extra buffers to pass to encoder or decoder initialization. + */ + mfxExtBuffer **ext_buffers; + int nb_ext_buffers; + + /** + * Encoding only. If this field is set to non-zero by the caller, libavcodec + * will create an mfxExtOpaqueSurfaceAlloc extended buffer and pass it to + * the encoder initialization. This only makes sense if iopattern is also + * set to MFX_IOPATTERN_IN_OPAQUE_MEMORY. + * + * The number of allocated opaque surfaces will be the sum of the number + * required by the encoder and the user-provided value nb_opaque_surfaces. + * The array of the opaque surfaces will be exported to the caller through + * the opaque_surfaces field. + */ + int opaque_alloc; + + /** + * Encoding only, and only if opaque_alloc is set to non-zero. Before + * calling avcodec_open2(), the caller should set this field to the number + * of extra opaque surfaces to allocate beyond what is required by the + * encoder. + * + * On return from avcodec_open2(), this field will be set by libavcodec to + * the total number of allocated opaque surfaces. + */ + int nb_opaque_surfaces; + + /** + * Encoding only, and only if opaque_alloc is set to non-zero. On return + * from avcodec_open2(), this field will be used by libavcodec to export the + * array of the allocated opaque surfaces to the caller, so they can be + * passed to other parts of the pipeline. + * + * The buffer reference exported here is owned and managed by libavcodec, + * the callers should make their own reference with av_buffer_ref() and free + * it with av_buffer_unref() when it is no longer needed. + * + * The buffer data is an nb_opaque_surfaces-sized array of mfxFrameSurface1. + */ + AVBufferRef *opaque_surfaces; + + /** + * Encoding only, and only if opaque_alloc is set to non-zero. On return + * from avcodec_open2(), this field will be set to the surface type used in + * the opaque allocation request. + */ + int opaque_alloc_type; +} AVQSVContext; + +/** + * Allocate a new context. + * + * It must be freed by the caller with av_free(). + */ +AVQSVContext *av_qsv_alloc_context(void); + +#endif /* AVCODEC_QSV_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vaapi.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vaapi.h new file mode 100644 index 0000000..2cf7da5 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vaapi.h
@@ -0,0 +1,86 @@ +/* + * Video Acceleration API (shared data between FFmpeg and the video player) + * HW decode acceleration for MPEG-2, MPEG-4, H.264 and VC-1 + * + * Copyright (C) 2008-2009 Splitted-Desktop Systems + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VAAPI_H +#define AVCODEC_VAAPI_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_vaapi + * Public libavcodec VA API header. + */ + +#include <stdint.h> +#include "libavutil/attributes.h" +#include "version.h" + +#if FF_API_STRUCT_VAAPI_CONTEXT + +/** + * @defgroup lavc_codec_hwaccel_vaapi VA API Decoding + * @ingroup lavc_codec_hwaccel + * @{ + */ + +/** + * This structure is used to share data between the FFmpeg library and + * the client video application. + * This shall be zero-allocated and available as + * AVCodecContext.hwaccel_context. All user members can be set once + * during initialization or through each AVCodecContext.get_buffer() + * function call. In any case, they must be valid prior to calling + * decoding functions. + * + * Deprecated: use AVCodecContext.hw_frames_ctx instead. + */ +struct attribute_deprecated vaapi_context { + /** + * Window system dependent data + * + * - encoding: unused + * - decoding: Set by user + */ + void *display; + + /** + * Configuration ID + * + * - encoding: unused + * - decoding: Set by user + */ + uint32_t config_id; + + /** + * Context ID (video decode pipeline) + * + * - encoding: unused + * - decoding: Set by user + */ + uint32_t context_id; +}; + +/* @} */ + +#endif /* FF_API_STRUCT_VAAPI_CONTEXT */ + +#endif /* AVCODEC_VAAPI_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vdpau.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vdpau.h new file mode 100644 index 0000000..4d99943 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vdpau.h
@@ -0,0 +1,176 @@ +/* + * The Video Decode and Presentation API for UNIX (VDPAU) is used for + * hardware-accelerated decoding of MPEG-1/2, H.264 and VC-1. + * + * Copyright (C) 2008 NVIDIA + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VDPAU_H +#define AVCODEC_VDPAU_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_vdpau + * Public libavcodec VDPAU header. + */ + + +/** + * @defgroup lavc_codec_hwaccel_vdpau VDPAU Decoder and Renderer + * @ingroup lavc_codec_hwaccel + * + * VDPAU hardware acceleration has two modules + * - VDPAU decoding + * - VDPAU presentation + * + * The VDPAU decoding module parses all headers using FFmpeg + * parsing mechanisms and uses VDPAU for the actual decoding. + * + * As per the current implementation, the actual decoding + * and rendering (API calls) are done as part of the VDPAU + * presentation (vo_vdpau.c) module. + * + * @{ + */ + +#include <vdpau/vdpau.h> + +#include "libavutil/avconfig.h" +#include "libavutil/attributes.h" + +#include "avcodec.h" +#include "version.h" + +struct AVCodecContext; +struct AVFrame; + +typedef int (*AVVDPAU_Render2)(struct AVCodecContext *, struct AVFrame *, + const VdpPictureInfo *, uint32_t, + const VdpBitstreamBuffer *); + +/** + * This structure is used to share data between the libavcodec library and + * the client video application. + * The user shall allocate the structure via the av_alloc_vdpau_hwaccel + * function and make it available as + * AVCodecContext.hwaccel_context. Members can be set by the user once + * during initialization or through each AVCodecContext.get_buffer() + * function call. In any case, they must be valid prior to calling + * decoding functions. + * + * The size of this structure is not a part of the public ABI and must not + * be used outside of libavcodec. Use av_vdpau_alloc_context() to allocate an + * AVVDPAUContext. + */ +typedef struct AVVDPAUContext { + /** + * VDPAU decoder handle + * + * Set by user. + */ + VdpDecoder decoder; + + /** + * VDPAU decoder render callback + * + * Set by the user. + */ + VdpDecoderRender *render; + + AVVDPAU_Render2 render2; +} AVVDPAUContext; + +/** + * @brief allocation function for AVVDPAUContext + * + * Allows extending the struct without breaking API/ABI + */ +AVVDPAUContext *av_alloc_vdpaucontext(void); + +AVVDPAU_Render2 av_vdpau_hwaccel_get_render2(const AVVDPAUContext *); +void av_vdpau_hwaccel_set_render2(AVVDPAUContext *, AVVDPAU_Render2); + +/** + * Associate a VDPAU device with a codec context for hardware acceleration. + * This function is meant to be called from the get_format() codec callback, + * or earlier. It can also be called after avcodec_flush_buffers() to change + * the underlying VDPAU device mid-stream (e.g. to recover from non-transparent + * display preemption). + * + * @note get_format() must return AV_PIX_FMT_VDPAU if this function completes + * successfully. + * + * @param avctx decoding context whose get_format() callback is invoked + * @param device VDPAU device handle to use for hardware acceleration + * @param get_proc_address VDPAU device driver + * @param flags zero of more OR'd AV_HWACCEL_FLAG_* flags + * + * @return 0 on success, an AVERROR code on failure. + */ +int av_vdpau_bind_context(AVCodecContext *avctx, VdpDevice device, + VdpGetProcAddress *get_proc_address, unsigned flags); + +/** + * Gets the parameters to create an adequate VDPAU video surface for the codec + * context using VDPAU hardware decoding acceleration. + * + * @note Behavior is undefined if the context was not successfully bound to a + * VDPAU device using av_vdpau_bind_context(). + * + * @param avctx the codec context being used for decoding the stream + * @param type storage space for the VDPAU video surface chroma type + * (or NULL to ignore) + * @param width storage space for the VDPAU video surface pixel width + * (or NULL to ignore) + * @param height storage space for the VDPAU video surface pixel height + * (or NULL to ignore) + * + * @return 0 on success, a negative AVERROR code on failure. + */ +int av_vdpau_get_surface_parameters(AVCodecContext *avctx, VdpChromaType *type, + uint32_t *width, uint32_t *height); + +/** + * Allocate an AVVDPAUContext. + * + * @return Newly-allocated AVVDPAUContext or NULL on failure. + */ +AVVDPAUContext *av_vdpau_alloc_context(void); + +#if FF_API_VDPAU_PROFILE +/** + * Get a decoder profile that should be used for initializing a VDPAU decoder. + * Should be called from the AVCodecContext.get_format() callback. + * + * @deprecated Use av_vdpau_bind_context() instead. + * + * @param avctx the codec context being used for decoding the stream + * @param profile a pointer into which the result will be written on success. + * The contents of profile are undefined if this function returns + * an error. + * + * @return 0 on success (non-negative), a negative AVERROR on failure. + */ +attribute_deprecated +int av_vdpau_get_profile(AVCodecContext *avctx, VdpDecoderProfile *profile); +#endif + +/* @}*/ + +#endif /* AVCODEC_VDPAU_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/version.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/version.h new file mode 100644 index 0000000..782ba97 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/version.h
@@ -0,0 +1,137 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VERSION_H +#define AVCODEC_VERSION_H + +/** + * @file + * @ingroup libavc + * Libavcodec version macros. + */ + +#include "libavutil/version.h" + +#define LIBAVCODEC_VERSION_MAJOR 58 +#define LIBAVCODEC_VERSION_MINOR 35 +#define LIBAVCODEC_VERSION_MICRO 100 + +#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ + LIBAVCODEC_VERSION_MINOR, \ + LIBAVCODEC_VERSION_MICRO) +#define LIBAVCODEC_VERSION AV_VERSION(LIBAVCODEC_VERSION_MAJOR, \ + LIBAVCODEC_VERSION_MINOR, \ + LIBAVCODEC_VERSION_MICRO) +#define LIBAVCODEC_BUILD LIBAVCODEC_VERSION_INT + +#define LIBAVCODEC_IDENT "Lavc" AV_STRINGIFY(LIBAVCODEC_VERSION) + +/** + * FF_API_* defines may be placed below to indicate public API that will be + * dropped at a future version bump. The defines themselves are not part of + * the public API and may change, break or disappear at any time. + * + * @note, when bumping the major version it is recommended to manually + * disable each FF_API_* in its own commit instead of disabling them all + * at once through the bump. This improves the git bisect-ability of the change. + */ + +#ifndef FF_API_LOWRES +#define FF_API_LOWRES (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_DEBUG_MV +#define FF_API_DEBUG_MV (LIBAVCODEC_VERSION_MAJOR < 58) +#endif +#ifndef FF_API_AVCTX_TIMEBASE +#define FF_API_AVCTX_TIMEBASE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_CODED_FRAME +#define FF_API_CODED_FRAME (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_SIDEDATA_ONLY_PKT +#define FF_API_SIDEDATA_ONLY_PKT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_VDPAU_PROFILE +#define FF_API_VDPAU_PROFILE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_CONVERGENCE_DURATION +#define FF_API_CONVERGENCE_DURATION (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_AVPICTURE +#define FF_API_AVPICTURE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_AVPACKET_OLD_API +#define FF_API_AVPACKET_OLD_API (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_RTP_CALLBACK +#define FF_API_RTP_CALLBACK (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_VBV_DELAY +#define FF_API_VBV_DELAY (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_CODER_TYPE +#define FF_API_CODER_TYPE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_STAT_BITS +#define FF_API_STAT_BITS (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_PRIVATE_OPT +#define FF_API_PRIVATE_OPT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_ASS_TIMING +#define FF_API_ASS_TIMING (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_BSF +#define FF_API_OLD_BSF (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_COPY_CONTEXT +#define FF_API_COPY_CONTEXT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_GET_CONTEXT_DEFAULTS +#define FF_API_GET_CONTEXT_DEFAULTS (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_NVENC_OLD_NAME +#define FF_API_NVENC_OLD_NAME (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_STRUCT_VAAPI_CONTEXT +#define FF_API_STRUCT_VAAPI_CONTEXT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_MERGE_SD_API +#define FF_API_MERGE_SD_API (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_TAG_STRING +#define FF_API_TAG_STRING (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_GETCHROMA +#define FF_API_GETCHROMA (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_CODEC_GET_SET +#define FF_API_CODEC_GET_SET (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_USER_VISIBLE_AVHWACCEL +#define FF_API_USER_VISIBLE_AVHWACCEL (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LOCKMGR +#define FF_API_LOCKMGR (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_NEXT +#define FF_API_NEXT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif + + +#endif /* AVCODEC_VERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/videotoolbox.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/videotoolbox.h new file mode 100644 index 0000000..af2db0d --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/videotoolbox.h
@@ -0,0 +1,127 @@ +/* + * Videotoolbox hardware acceleration + * + * copyright (c) 2012 Sebastien Zwickert + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VIDEOTOOLBOX_H +#define AVCODEC_VIDEOTOOLBOX_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_videotoolbox + * Public libavcodec Videotoolbox header. + */ + +#include <stdint.h> + +#define Picture QuickdrawPicture +#include <VideoToolbox/VideoToolbox.h> +#undef Picture + +#include "libavcodec/avcodec.h" + +/** + * This struct holds all the information that needs to be passed + * between the caller and libavcodec for initializing Videotoolbox decoding. + * Its size is not a part of the public ABI, it must be allocated with + * av_videotoolbox_alloc_context() and freed with av_free(). + */ +typedef struct AVVideotoolboxContext { + /** + * Videotoolbox decompression session object. + * Created and freed the caller. + */ + VTDecompressionSessionRef session; + + /** + * The output callback that must be passed to the session. + * Set by av_videottoolbox_default_init() + */ + VTDecompressionOutputCallback output_callback; + + /** + * CVPixelBuffer Format Type that Videotoolbox will use for decoded frames. + * set by the caller. If this is set to 0, then no specific format is + * requested from the decoder, and its native format is output. + */ + OSType cv_pix_fmt_type; + + /** + * CoreMedia Format Description that Videotoolbox will use to create the decompression session. + * Set by the caller. + */ + CMVideoFormatDescriptionRef cm_fmt_desc; + + /** + * CoreMedia codec type that Videotoolbox will use to create the decompression session. + * Set by the caller. + */ + int cm_codec_type; +} AVVideotoolboxContext; + +/** + * Allocate and initialize a Videotoolbox context. + * + * This function should be called from the get_format() callback when the caller + * selects the AV_PIX_FMT_VIDETOOLBOX format. The caller must then create + * the decoder object (using the output callback provided by libavcodec) that + * will be used for Videotoolbox-accelerated decoding. + * + * When decoding with Videotoolbox is finished, the caller must destroy the decoder + * object and free the Videotoolbox context using av_free(). + * + * @return the newly allocated context or NULL on failure + */ +AVVideotoolboxContext *av_videotoolbox_alloc_context(void); + +/** + * This is a convenience function that creates and sets up the Videotoolbox context using + * an internal implementation. + * + * @param avctx the corresponding codec context + * + * @return >= 0 on success, a negative AVERROR code on failure + */ +int av_videotoolbox_default_init(AVCodecContext *avctx); + +/** + * This is a convenience function that creates and sets up the Videotoolbox context using + * an internal implementation. + * + * @param avctx the corresponding codec context + * @param vtctx the Videotoolbox context to use + * + * @return >= 0 on success, a negative AVERROR code on failure + */ +int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext *vtctx); + +/** + * This function must be called to free the Videotoolbox context initialized with + * av_videotoolbox_default_init(). + * + * @param avctx the corresponding codec context + */ +void av_videotoolbox_default_free(AVCodecContext *avctx); + +/** + * @} + */ + +#endif /* AVCODEC_VIDEOTOOLBOX_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vorbis_parser.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vorbis_parser.h new file mode 100644 index 0000000..789932a --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vorbis_parser.h
@@ -0,0 +1,74 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * A public API for Vorbis parsing + * + * Determines the duration for each packet. + */ + +#ifndef AVCODEC_VORBIS_PARSER_H +#define AVCODEC_VORBIS_PARSER_H + +#include <stdint.h> + +typedef struct AVVorbisParseContext AVVorbisParseContext; + +/** + * Allocate and initialize the Vorbis parser using headers in the extradata. + */ +AVVorbisParseContext *av_vorbis_parse_init(const uint8_t *extradata, + int extradata_size); + +/** + * Free the parser and everything associated with it. + */ +void av_vorbis_parse_free(AVVorbisParseContext **s); + +#define VORBIS_FLAG_HEADER 0x00000001 +#define VORBIS_FLAG_COMMENT 0x00000002 +#define VORBIS_FLAG_SETUP 0x00000004 + +/** + * Get the duration for a Vorbis packet. + * + * If @p flags is @c NULL, + * special frames are considered invalid. + * + * @param s Vorbis parser context + * @param buf buffer containing a Vorbis frame + * @param buf_size size of the buffer + * @param flags flags for special frames + */ +int av_vorbis_parse_frame_flags(AVVorbisParseContext *s, const uint8_t *buf, + int buf_size, int *flags); + +/** + * Get the duration for a Vorbis packet. + * + * @param s Vorbis parser context + * @param buf buffer containing a Vorbis frame + * @param buf_size size of the buffer + */ +int av_vorbis_parse_frame(AVVorbisParseContext *s, const uint8_t *buf, + int buf_size); + +void av_vorbis_parse_reset(AVVorbisParseContext *s); + +#endif /* AVCODEC_VORBIS_PARSER_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/xvmc.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/xvmc.h new file mode 100644 index 0000000..465ee78 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/xvmc.h
@@ -0,0 +1,170 @@ +/* + * Copyright (C) 2003 Ivan Kalvachev + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_XVMC_H +#define AVCODEC_XVMC_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_xvmc + * Public libavcodec XvMC header. + */ + +#include <X11/extensions/XvMC.h> + +#include "libavutil/attributes.h" +#include "version.h" +#include "avcodec.h" + +/** + * @defgroup lavc_codec_hwaccel_xvmc XvMC + * @ingroup lavc_codec_hwaccel + * + * @{ + */ + +#define AV_XVMC_ID 0x1DC711C0 /**< special value to ensure that regular pixel routines haven't corrupted the struct + the number is 1337 speak for the letters IDCT MCo (motion compensation) */ + +struct attribute_deprecated xvmc_pix_fmt { + /** The field contains the special constant value AV_XVMC_ID. + It is used as a test that the application correctly uses the API, + and that there is no corruption caused by pixel routines. + - application - set during initialization + - libavcodec - unchanged + */ + int xvmc_id; + + /** Pointer to the block array allocated by XvMCCreateBlocks(). + The array has to be freed by XvMCDestroyBlocks(). + Each group of 64 values represents one data block of differential + pixel information (in MoCo mode) or coefficients for IDCT. + - application - set the pointer during initialization + - libavcodec - fills coefficients/pixel data into the array + */ + short* data_blocks; + + /** Pointer to the macroblock description array allocated by + XvMCCreateMacroBlocks() and freed by XvMCDestroyMacroBlocks(). + - application - set the pointer during initialization + - libavcodec - fills description data into the array + */ + XvMCMacroBlock* mv_blocks; + + /** Number of macroblock descriptions that can be stored in the mv_blocks + array. + - application - set during initialization + - libavcodec - unchanged + */ + int allocated_mv_blocks; + + /** Number of blocks that can be stored at once in the data_blocks array. + - application - set during initialization + - libavcodec - unchanged + */ + int allocated_data_blocks; + + /** Indicate that the hardware would interpret data_blocks as IDCT + coefficients and perform IDCT on them. + - application - set during initialization + - libavcodec - unchanged + */ + int idct; + + /** In MoCo mode it indicates that intra macroblocks are assumed to be in + unsigned format; same as the XVMC_INTRA_UNSIGNED flag. + - application - set during initialization + - libavcodec - unchanged + */ + int unsigned_intra; + + /** Pointer to the surface allocated by XvMCCreateSurface(). + It has to be freed by XvMCDestroySurface() on application exit. + It identifies the frame and its state on the video hardware. + - application - set during initialization + - libavcodec - unchanged + */ + XvMCSurface* p_surface; + +/** Set by the decoder before calling ff_draw_horiz_band(), + needed by the XvMCRenderSurface function. */ +//@{ + /** Pointer to the surface used as past reference + - application - unchanged + - libavcodec - set + */ + XvMCSurface* p_past_surface; + + /** Pointer to the surface used as future reference + - application - unchanged + - libavcodec - set + */ + XvMCSurface* p_future_surface; + + /** top/bottom field or frame + - application - unchanged + - libavcodec - set + */ + unsigned int picture_structure; + + /** XVMC_SECOND_FIELD - 1st or 2nd field in the sequence + - application - unchanged + - libavcodec - set + */ + unsigned int flags; +//}@ + + /** Number of macroblock descriptions in the mv_blocks array + that have already been passed to the hardware. + - application - zeroes it on get_buffer(). + A successful ff_draw_horiz_band() may increment it + with filled_mb_block_num or zero both. + - libavcodec - unchanged + */ + int start_mv_blocks_num; + + /** Number of new macroblock descriptions in the mv_blocks array (after + start_mv_blocks_num) that are filled by libavcodec and have to be + passed to the hardware. + - application - zeroes it on get_buffer() or after successful + ff_draw_horiz_band(). + - libavcodec - increment with one of each stored MB + */ + int filled_mv_blocks_num; + + /** Number of the next free data block; one data block consists of + 64 short values in the data_blocks array. + All blocks before this one have already been claimed by placing their + position into the corresponding block description structure field, + that are part of the mv_blocks array. + - application - zeroes it on get_buffer(). + A successful ff_draw_horiz_band() may zero it together + with start_mb_blocks_num. + - libavcodec - each decoded macroblock increases it by the number + of coded blocks it contains. + */ + int next_free_data_block_num; +}; + +/** + * @} + */ + +#endif /* AVCODEC_XVMC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avformat.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avformat.h new file mode 100644 index 0000000..fdaffa5 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avformat.h
@@ -0,0 +1,3085 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AVFORMAT_H +#define AVFORMAT_AVFORMAT_H + +/** + * @file + * @ingroup libavf + * Main libavformat public API header + */ + +/** + * @defgroup libavf libavformat + * I/O and Muxing/Demuxing Library + * + * Libavformat (lavf) is a library for dealing with various media container + * formats. Its main two purposes are demuxing - i.e. splitting a media file + * into component streams, and the reverse process of muxing - writing supplied + * data in a specified container format. It also has an @ref lavf_io + * "I/O module" which supports a number of protocols for accessing the data (e.g. + * file, tcp, http and others). Before using lavf, you need to call + * av_register_all() to register all compiled muxers, demuxers and protocols. + * Unless you are absolutely sure you won't use libavformat's network + * capabilities, you should also call avformat_network_init(). + * + * A supported input format is described by an AVInputFormat struct, conversely + * an output format is described by AVOutputFormat. You can iterate over all + * registered input/output formats using the av_iformat_next() / + * av_oformat_next() functions. The protocols layer is not part of the public + * API, so you can only get the names of supported protocols with the + * avio_enum_protocols() function. + * + * Main lavf structure used for both muxing and demuxing is AVFormatContext, + * which exports all information about the file being read or written. As with + * most Libavformat structures, its size is not part of public ABI, so it cannot be + * allocated on stack or directly with av_malloc(). To create an + * AVFormatContext, use avformat_alloc_context() (some functions, like + * avformat_open_input() might do that for you). + * + * Most importantly an AVFormatContext contains: + * @li the @ref AVFormatContext.iformat "input" or @ref AVFormatContext.oformat + * "output" format. It is either autodetected or set by user for input; + * always set by user for output. + * @li an @ref AVFormatContext.streams "array" of AVStreams, which describe all + * elementary streams stored in the file. AVStreams are typically referred to + * using their index in this array. + * @li an @ref AVFormatContext.pb "I/O context". It is either opened by lavf or + * set by user for input, always set by user for output (unless you are dealing + * with an AVFMT_NOFILE format). + * + * @section lavf_options Passing options to (de)muxers + * It is possible to configure lavf muxers and demuxers using the @ref avoptions + * mechanism. Generic (format-independent) libavformat options are provided by + * AVFormatContext, they can be examined from a user program by calling + * av_opt_next() / av_opt_find() on an allocated AVFormatContext (or its AVClass + * from avformat_get_class()). Private (format-specific) options are provided by + * AVFormatContext.priv_data if and only if AVInputFormat.priv_class / + * AVOutputFormat.priv_class of the corresponding format struct is non-NULL. + * Further options may be provided by the @ref AVFormatContext.pb "I/O context", + * if its AVClass is non-NULL, and the protocols layer. See the discussion on + * nesting in @ref avoptions documentation to learn how to access those. + * + * @section urls + * URL strings in libavformat are made of a scheme/protocol, a ':', and a + * scheme specific string. URLs without a scheme and ':' used for local files + * are supported but deprecated. "file:" should be used for local files. + * + * It is important that the scheme string is not taken from untrusted + * sources without checks. + * + * Note that some schemes/protocols are quite powerful, allowing access to + * both local and remote files, parts of them, concatenations of them, local + * audio and video devices and so on. + * + * @{ + * + * @defgroup lavf_decoding Demuxing + * @{ + * Demuxers read a media file and split it into chunks of data (@em packets). A + * @ref AVPacket "packet" contains one or more encoded frames which belongs to a + * single elementary stream. In the lavf API this process is represented by the + * avformat_open_input() function for opening a file, av_read_frame() for + * reading a single packet and finally avformat_close_input(), which does the + * cleanup. + * + * @section lavf_decoding_open Opening a media file + * The minimum information required to open a file is its URL, which + * is passed to avformat_open_input(), as in the following code: + * @code + * const char *url = "file:in.mp3"; + * AVFormatContext *s = NULL; + * int ret = avformat_open_input(&s, url, NULL, NULL); + * if (ret < 0) + * abort(); + * @endcode + * The above code attempts to allocate an AVFormatContext, open the + * specified file (autodetecting the format) and read the header, exporting the + * information stored there into s. Some formats do not have a header or do not + * store enough information there, so it is recommended that you call the + * avformat_find_stream_info() function which tries to read and decode a few + * frames to find missing information. + * + * In some cases you might want to preallocate an AVFormatContext yourself with + * avformat_alloc_context() and do some tweaking on it before passing it to + * avformat_open_input(). One such case is when you want to use custom functions + * for reading input data instead of lavf internal I/O layer. + * To do that, create your own AVIOContext with avio_alloc_context(), passing + * your reading callbacks to it. Then set the @em pb field of your + * AVFormatContext to newly created AVIOContext. + * + * Since the format of the opened file is in general not known until after + * avformat_open_input() has returned, it is not possible to set demuxer private + * options on a preallocated context. Instead, the options should be passed to + * avformat_open_input() wrapped in an AVDictionary: + * @code + * AVDictionary *options = NULL; + * av_dict_set(&options, "video_size", "640x480", 0); + * av_dict_set(&options, "pixel_format", "rgb24", 0); + * + * if (avformat_open_input(&s, url, NULL, &options) < 0) + * abort(); + * av_dict_free(&options); + * @endcode + * This code passes the private options 'video_size' and 'pixel_format' to the + * demuxer. They would be necessary for e.g. the rawvideo demuxer, since it + * cannot know how to interpret raw video data otherwise. If the format turns + * out to be something different than raw video, those options will not be + * recognized by the demuxer and therefore will not be applied. Such unrecognized + * options are then returned in the options dictionary (recognized options are + * consumed). The calling program can handle such unrecognized options as it + * wishes, e.g. + * @code + * AVDictionaryEntry *e; + * if (e = av_dict_get(options, "", NULL, AV_DICT_IGNORE_SUFFIX)) { + * fprintf(stderr, "Option %s not recognized by the demuxer.\n", e->key); + * abort(); + * } + * @endcode + * + * After you have finished reading the file, you must close it with + * avformat_close_input(). It will free everything associated with the file. + * + * @section lavf_decoding_read Reading from an opened file + * Reading data from an opened AVFormatContext is done by repeatedly calling + * av_read_frame() on it. Each call, if successful, will return an AVPacket + * containing encoded data for one AVStream, identified by + * AVPacket.stream_index. This packet may be passed straight into the libavcodec + * decoding functions avcodec_send_packet() or avcodec_decode_subtitle2() if the + * caller wishes to decode the data. + * + * AVPacket.pts, AVPacket.dts and AVPacket.duration timing information will be + * set if known. They may also be unset (i.e. AV_NOPTS_VALUE for + * pts/dts, 0 for duration) if the stream does not provide them. The timing + * information will be in AVStream.time_base units, i.e. it has to be + * multiplied by the timebase to convert them to seconds. + * + * If AVPacket.buf is set on the returned packet, then the packet is + * allocated dynamically and the user may keep it indefinitely. + * Otherwise, if AVPacket.buf is NULL, the packet data is backed by a + * static storage somewhere inside the demuxer and the packet is only valid + * until the next av_read_frame() call or closing the file. If the caller + * requires a longer lifetime, av_dup_packet() will make an av_malloc()ed copy + * of it. + * In both cases, the packet must be freed with av_packet_unref() when it is no + * longer needed. + * + * @section lavf_decoding_seek Seeking + * @} + * + * @defgroup lavf_encoding Muxing + * @{ + * Muxers take encoded data in the form of @ref AVPacket "AVPackets" and write + * it into files or other output bytestreams in the specified container format. + * + * The main API functions for muxing are avformat_write_header() for writing the + * file header, av_write_frame() / av_interleaved_write_frame() for writing the + * packets and av_write_trailer() for finalizing the file. + * + * At the beginning of the muxing process, the caller must first call + * avformat_alloc_context() to create a muxing context. The caller then sets up + * the muxer by filling the various fields in this context: + * + * - The @ref AVFormatContext.oformat "oformat" field must be set to select the + * muxer that will be used. + * - Unless the format is of the AVFMT_NOFILE type, the @ref AVFormatContext.pb + * "pb" field must be set to an opened IO context, either returned from + * avio_open2() or a custom one. + * - Unless the format is of the AVFMT_NOSTREAMS type, at least one stream must + * be created with the avformat_new_stream() function. The caller should fill + * the @ref AVStream.codecpar "stream codec parameters" information, such as the + * codec @ref AVCodecParameters.codec_type "type", @ref AVCodecParameters.codec_id + * "id" and other parameters (e.g. width / height, the pixel or sample format, + * etc.) as known. The @ref AVStream.time_base "stream timebase" should + * be set to the timebase that the caller desires to use for this stream (note + * that the timebase actually used by the muxer can be different, as will be + * described later). + * - It is advised to manually initialize only the relevant fields in + * AVCodecParameters, rather than using @ref avcodec_parameters_copy() during + * remuxing: there is no guarantee that the codec context values remain valid + * for both input and output format contexts. + * - The caller may fill in additional information, such as @ref + * AVFormatContext.metadata "global" or @ref AVStream.metadata "per-stream" + * metadata, @ref AVFormatContext.chapters "chapters", @ref + * AVFormatContext.programs "programs", etc. as described in the + * AVFormatContext documentation. Whether such information will actually be + * stored in the output depends on what the container format and the muxer + * support. + * + * When the muxing context is fully set up, the caller must call + * avformat_write_header() to initialize the muxer internals and write the file + * header. Whether anything actually is written to the IO context at this step + * depends on the muxer, but this function must always be called. Any muxer + * private options must be passed in the options parameter to this function. + * + * The data is then sent to the muxer by repeatedly calling av_write_frame() or + * av_interleaved_write_frame() (consult those functions' documentation for + * discussion on the difference between them; only one of them may be used with + * a single muxing context, they should not be mixed). Do note that the timing + * information on the packets sent to the muxer must be in the corresponding + * AVStream's timebase. That timebase is set by the muxer (in the + * avformat_write_header() step) and may be different from the timebase + * requested by the caller. + * + * Once all the data has been written, the caller must call av_write_trailer() + * to flush any buffered packets and finalize the output file, then close the IO + * context (if any) and finally free the muxing context with + * avformat_free_context(). + * @} + * + * @defgroup lavf_io I/O Read/Write + * @{ + * @section lavf_io_dirlist Directory listing + * The directory listing API makes it possible to list files on remote servers. + * + * Some of possible use cases: + * - an "open file" dialog to choose files from a remote location, + * - a recursive media finder providing a player with an ability to play all + * files from a given directory. + * + * @subsection lavf_io_dirlist_open Opening a directory + * At first, a directory needs to be opened by calling avio_open_dir() + * supplied with a URL and, optionally, ::AVDictionary containing + * protocol-specific parameters. The function returns zero or positive + * integer and allocates AVIODirContext on success. + * + * @code + * AVIODirContext *ctx = NULL; + * if (avio_open_dir(&ctx, "smb://example.com/some_dir", NULL) < 0) { + * fprintf(stderr, "Cannot open directory.\n"); + * abort(); + * } + * @endcode + * + * This code tries to open a sample directory using smb protocol without + * any additional parameters. + * + * @subsection lavf_io_dirlist_read Reading entries + * Each directory's entry (i.e. file, another directory, anything else + * within ::AVIODirEntryType) is represented by AVIODirEntry. + * Reading consecutive entries from an opened AVIODirContext is done by + * repeatedly calling avio_read_dir() on it. Each call returns zero or + * positive integer if successful. Reading can be stopped right after the + * NULL entry has been read -- it means there are no entries left to be + * read. The following code reads all entries from a directory associated + * with ctx and prints their names to standard output. + * @code + * AVIODirEntry *entry = NULL; + * for (;;) { + * if (avio_read_dir(ctx, &entry) < 0) { + * fprintf(stderr, "Cannot list directory.\n"); + * abort(); + * } + * if (!entry) + * break; + * printf("%s\n", entry->name); + * avio_free_directory_entry(&entry); + * } + * @endcode + * @} + * + * @defgroup lavf_codec Demuxers + * @{ + * @defgroup lavf_codec_native Native Demuxers + * @{ + * @} + * @defgroup lavf_codec_wrappers External library wrappers + * @{ + * @} + * @} + * @defgroup lavf_protos I/O Protocols + * @{ + * @} + * @defgroup lavf_internal Internal + * @{ + * @} + * @} + */ + +#include <time.h> +#include <stdio.h> /* FILE */ +#include "libavcodec/avcodec.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" + +#include "avio.h" +#include "libavformat/version.h" + +struct AVFormatContext; + +struct AVDeviceInfoList; +struct AVDeviceCapabilitiesQuery; + +/** + * @defgroup metadata_api Public Metadata API + * @{ + * @ingroup libavf + * The metadata API allows libavformat to export metadata tags to a client + * application when demuxing. Conversely it allows a client application to + * set metadata when muxing. + * + * Metadata is exported or set as pairs of key/value strings in the 'metadata' + * fields of the AVFormatContext, AVStream, AVChapter and AVProgram structs + * using the @ref lavu_dict "AVDictionary" API. Like all strings in FFmpeg, + * metadata is assumed to be UTF-8 encoded Unicode. Note that metadata + * exported by demuxers isn't checked to be valid UTF-8 in most cases. + * + * Important concepts to keep in mind: + * - Keys are unique; there can never be 2 tags with the same key. This is + * also meant semantically, i.e., a demuxer should not knowingly produce + * several keys that are literally different but semantically identical. + * E.g., key=Author5, key=Author6. In this example, all authors must be + * placed in the same tag. + * - Metadata is flat, not hierarchical; there are no subtags. If you + * want to store, e.g., the email address of the child of producer Alice + * and actor Bob, that could have key=alice_and_bobs_childs_email_address. + * - Several modifiers can be applied to the tag name. This is done by + * appending a dash character ('-') and the modifier name in the order + * they appear in the list below -- e.g. foo-eng-sort, not foo-sort-eng. + * - language -- a tag whose value is localized for a particular language + * is appended with the ISO 639-2/B 3-letter language code. + * For example: Author-ger=Michael, Author-eng=Mike + * The original/default language is in the unqualified "Author" tag. + * A demuxer should set a default if it sets any translated tag. + * - sorting -- a modified version of a tag that should be used for + * sorting will have '-sort' appended. E.g. artist="The Beatles", + * artist-sort="Beatles, The". + * - Some protocols and demuxers support metadata updates. After a successful + * call to av_read_packet(), AVFormatContext.event_flags or AVStream.event_flags + * will be updated to indicate if metadata changed. In order to detect metadata + * changes on a stream, you need to loop through all streams in the AVFormatContext + * and check their individual event_flags. + * + * - Demuxers attempt to export metadata in a generic format, however tags + * with no generic equivalents are left as they are stored in the container. + * Follows a list of generic tag names: + * + @verbatim + album -- name of the set this work belongs to + album_artist -- main creator of the set/album, if different from artist. + e.g. "Various Artists" for compilation albums. + artist -- main creator of the work + comment -- any additional description of the file. + composer -- who composed the work, if different from artist. + copyright -- name of copyright holder. + creation_time-- date when the file was created, preferably in ISO 8601. + date -- date when the work was created, preferably in ISO 8601. + disc -- number of a subset, e.g. disc in a multi-disc collection. + encoder -- name/settings of the software/hardware that produced the file. + encoded_by -- person/group who created the file. + filename -- original name of the file. + genre -- <self-evident>. + language -- main language in which the work is performed, preferably + in ISO 639-2 format. Multiple languages can be specified by + separating them with commas. + performer -- artist who performed the work, if different from artist. + E.g for "Also sprach Zarathustra", artist would be "Richard + Strauss" and performer "London Philharmonic Orchestra". + publisher -- name of the label/publisher. + service_name -- name of the service in broadcasting (channel name). + service_provider -- name of the service provider in broadcasting. + title -- name of the work. + track -- number of this work in the set, can be in form current/total. + variant_bitrate -- the total bitrate of the bitrate variant that the current stream is part of + @endverbatim + * + * Look in the examples section for an application example how to use the Metadata API. + * + * @} + */ + +/* packet functions */ + + +/** + * Allocate and read the payload of a packet and initialize its + * fields with default values. + * + * @param s associated IO context + * @param pkt packet + * @param size desired payload size + * @return >0 (read size) if OK, AVERROR_xxx otherwise + */ +int av_get_packet(AVIOContext *s, AVPacket *pkt, int size); + + +/** + * Read data and append it to the current content of the AVPacket. + * If pkt->size is 0 this is identical to av_get_packet. + * Note that this uses av_grow_packet and thus involves a realloc + * which is inefficient. Thus this function should only be used + * when there is no reasonable way to know (an upper bound of) + * the final size. + * + * @param s associated IO context + * @param pkt packet + * @param size amount of data to read + * @return >0 (read size) if OK, AVERROR_xxx otherwise, previous data + * will not be lost even if an error occurs. + */ +int av_append_packet(AVIOContext *s, AVPacket *pkt, int size); + +/*************************************************/ +/* input/output formats */ + +struct AVCodecTag; + +/** + * This structure contains the data a format has to probe a file. + */ +typedef struct AVProbeData { + const char *filename; + unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */ + int buf_size; /**< Size of buf except extra allocated bytes */ + const char *mime_type; /**< mime_type, when known. */ +} AVProbeData; + +#define AVPROBE_SCORE_RETRY (AVPROBE_SCORE_MAX/4) +#define AVPROBE_SCORE_STREAM_RETRY (AVPROBE_SCORE_MAX/4-1) + +#define AVPROBE_SCORE_EXTENSION 50 ///< score for file extension +#define AVPROBE_SCORE_MIME 75 ///< score for file mime type +#define AVPROBE_SCORE_MAX 100 ///< maximum score + +#define AVPROBE_PADDING_SIZE 32 ///< extra allocated bytes at the end of the probe buffer + +/// Demuxer will use avio_open, no opened file should be provided by the caller. +#define AVFMT_NOFILE 0x0001 +#define AVFMT_NEEDNUMBER 0x0002 /**< Needs '%d' in filename. */ +#define AVFMT_SHOW_IDS 0x0008 /**< Show format stream IDs numbers. */ +#define AVFMT_GLOBALHEADER 0x0040 /**< Format wants global header. */ +#define AVFMT_NOTIMESTAMPS 0x0080 /**< Format does not need / have any timestamps. */ +#define AVFMT_GENERIC_INDEX 0x0100 /**< Use generic index building code. */ +#define AVFMT_TS_DISCONT 0x0200 /**< Format allows timestamp discontinuities. Note, muxers always require valid (monotone) timestamps */ +#define AVFMT_VARIABLE_FPS 0x0400 /**< Format allows variable fps. */ +#define AVFMT_NODIMENSIONS 0x0800 /**< Format does not need width/height */ +#define AVFMT_NOSTREAMS 0x1000 /**< Format does not require any streams */ +#define AVFMT_NOBINSEARCH 0x2000 /**< Format does not allow to fall back on binary search via read_timestamp */ +#define AVFMT_NOGENSEARCH 0x4000 /**< Format does not allow to fall back on generic search */ +#define AVFMT_NO_BYTE_SEEK 0x8000 /**< Format does not allow seeking by bytes */ +#define AVFMT_ALLOW_FLUSH 0x10000 /**< Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function. */ +#define AVFMT_TS_NONSTRICT 0x20000 /**< Format does not require strictly + increasing timestamps, but they must + still be monotonic */ +#define AVFMT_TS_NEGATIVE 0x40000 /**< Format allows muxing negative + timestamps. If not set the timestamp + will be shifted in av_write_frame and + av_interleaved_write_frame so they + start from 0. + The user or muxer can override this through + AVFormatContext.avoid_negative_ts + */ + +#define AVFMT_SEEK_TO_PTS 0x4000000 /**< Seeking is based on PTS */ + +/** + * @addtogroup lavf_encoding + * @{ + */ +typedef struct AVOutputFormat { + const char *name; + /** + * Descriptive name for the format, meant to be more human-readable + * than name. You should use the NULL_IF_CONFIG_SMALL() macro + * to define it. + */ + const char *long_name; + const char *mime_type; + const char *extensions; /**< comma-separated filename extensions */ + /* output support */ + enum AVCodecID audio_codec; /**< default audio codec */ + enum AVCodecID video_codec; /**< default video codec */ + enum AVCodecID subtitle_codec; /**< default subtitle codec */ + /** + * can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, + * AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS, + * AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH, + * AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE + */ + int flags; + + /** + * List of supported codec_id-codec_tag pairs, ordered by "better + * choice first". The arrays are all terminated by AV_CODEC_ID_NONE. + */ + const struct AVCodecTag * const *codec_tag; + + + const AVClass *priv_class; ///< AVClass for the private context + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + struct AVOutputFormat *next; + /** + * size of private data so that it can be allocated in the wrapper + */ + int priv_data_size; + + int (*write_header)(struct AVFormatContext *); + /** + * Write a packet. If AVFMT_ALLOW_FLUSH is set in flags, + * pkt can be NULL in order to flush data buffered in the muxer. + * When flushing, return 0 if there still is more data to flush, + * or 1 if everything was flushed and there is no more buffered + * data. + */ + int (*write_packet)(struct AVFormatContext *, AVPacket *pkt); + int (*write_trailer)(struct AVFormatContext *); + /** + * Currently only used to set pixel format if not YUV420P. + */ + int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, + AVPacket *in, int flush); + /** + * Test if the given codec can be stored in this container. + * + * @return 1 if the codec is supported, 0 if it is not. + * A negative number if unknown. + * MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC + */ + int (*query_codec)(enum AVCodecID id, int std_compliance); + + void (*get_output_timestamp)(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); + /** + * Allows sending messages from application to device. + */ + int (*control_message)(struct AVFormatContext *s, int type, + void *data, size_t data_size); + + /** + * Write an uncoded AVFrame. + * + * See av_write_uncoded_frame() for details. + * + * The library will free *frame afterwards, but the muxer can prevent it + * by setting the pointer to NULL. + */ + int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index, + AVFrame **frame, unsigned flags); + /** + * Returns device list with it properties. + * @see avdevice_list_devices() for more details. + */ + int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); + /** + * Initialize device capabilities submodule. + * @see avdevice_capabilities_create() for more details. + */ + int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); + /** + * Free device capabilities submodule. + * @see avdevice_capabilities_free() for more details. + */ + int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); + enum AVCodecID data_codec; /**< default data codec */ + /** + * Initialize format. May allocate data here, and set any AVFormatContext or + * AVStream parameters that need to be set before packets are sent. + * This method must not write output. + * + * Return 0 if streams were fully configured, 1 if not, negative AVERROR on failure + * + * Any allocations made here must be freed in deinit(). + */ + int (*init)(struct AVFormatContext *); + /** + * Deinitialize format. If present, this is called whenever the muxer is being + * destroyed, regardless of whether or not the header has been written. + * + * If a trailer is being written, this is called after write_trailer(). + * + * This is called if init() fails as well. + */ + void (*deinit)(struct AVFormatContext *); + /** + * Set up any necessary bitstream filtering and extract any extra data needed + * for the global header. + * Return 0 if more packets from this stream must be checked; 1 if not. + */ + int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt); +} AVOutputFormat; +/** + * @} + */ + +/** + * @addtogroup lavf_decoding + * @{ + */ +typedef struct AVInputFormat { + /** + * A comma separated list of short names for the format. New names + * may be appended with a minor bump. + */ + const char *name; + + /** + * Descriptive name for the format, meant to be more human-readable + * than name. You should use the NULL_IF_CONFIG_SMALL() macro + * to define it. + */ + const char *long_name; + + /** + * Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS, + * AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH, + * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS. + */ + int flags; + + /** + * If extensions are defined, then no probe is done. You should + * usually not use extension format guessing because it is not + * reliable enough + */ + const char *extensions; + + const struct AVCodecTag * const *codec_tag; + + const AVClass *priv_class; ///< AVClass for the private context + + /** + * Comma-separated list of mime types. + * It is used check for matching mime types while probing. + * @see av_probe_input_format2 + */ + const char *mime_type; + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + struct AVInputFormat *next; + + /** + * Raw demuxers store their codec ID here. + */ + int raw_codec_id; + + /** + * Size of private data so that it can be allocated in the wrapper. + */ + int priv_data_size; + + /** + * Tell if a given file has a chance of being parsed as this format. + * The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes + * big so you do not have to check for that unless you need more. + */ + int (*read_probe)(AVProbeData *); + + /** + * Read the format header and initialize the AVFormatContext + * structure. Return 0 if OK. 'avformat_new_stream' should be + * called to create new streams. + */ + int (*read_header)(struct AVFormatContext *); + + /** + * Read one packet and put it in 'pkt'. pts and flags are also + * set. 'avformat_new_stream' can be called only if the flag + * AVFMTCTX_NOHEADER is used and only in the calling thread (not in a + * background thread). + * @return 0 on success, < 0 on error. + * When returning an error, pkt must not have been allocated + * or must be freed before returning + */ + int (*read_packet)(struct AVFormatContext *, AVPacket *pkt); + + /** + * Close the stream. The AVFormatContext and AVStreams are not + * freed by this function + */ + int (*read_close)(struct AVFormatContext *); + + /** + * Seek to a given timestamp relative to the frames in + * stream component stream_index. + * @param stream_index Must not be -1. + * @param flags Selects which direction should be preferred if no exact + * match is available. + * @return >= 0 on success (but not necessarily the new offset) + */ + int (*read_seek)(struct AVFormatContext *, + int stream_index, int64_t timestamp, int flags); + + /** + * Get the next timestamp in stream[stream_index].time_base units. + * @return the timestamp or AV_NOPTS_VALUE if an error occurred + */ + int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index, + int64_t *pos, int64_t pos_limit); + + /** + * Start/resume playing - only meaningful if using a network-based format + * (RTSP). + */ + int (*read_play)(struct AVFormatContext *); + + /** + * Pause playing - only meaningful if using a network-based format + * (RTSP). + */ + int (*read_pause)(struct AVFormatContext *); + + /** + * Seek to timestamp ts. + * Seeking will be done so that the point from which all active streams + * can be presented successfully will be closest to ts and within min/max_ts. + * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. + */ + int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); + + /** + * Returns device list with it properties. + * @see avdevice_list_devices() for more details. + */ + int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); + + /** + * Initialize device capabilities submodule. + * @see avdevice_capabilities_create() for more details. + */ + int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); + + /** + * Free device capabilities submodule. + * @see avdevice_capabilities_free() for more details. + */ + int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); +} AVInputFormat; +/** + * @} + */ + +enum AVStreamParseType { + AVSTREAM_PARSE_NONE, + AVSTREAM_PARSE_FULL, /**< full parsing and repack */ + AVSTREAM_PARSE_HEADERS, /**< Only parse headers, do not repack. */ + AVSTREAM_PARSE_TIMESTAMPS, /**< full parsing and interpolation of timestamps for frames not starting on a packet boundary */ + AVSTREAM_PARSE_FULL_ONCE, /**< full parsing and repack of the first frame only, only implemented for H.264 currently */ + AVSTREAM_PARSE_FULL_RAW, /**< full parsing and repack with timestamp and position generation by parser for raw + this assumes that each packet in the file contains no demuxer level headers and + just codec level data, otherwise position generation would fail */ +}; + +typedef struct AVIndexEntry { + int64_t pos; + int64_t timestamp; /**< + * Timestamp in AVStream.time_base units, preferably the time from which on correctly decoded frames are available + * when seeking to this entry. That means preferable PTS on keyframe based formats. + * But demuxers can choose to store a different timestamp, if it is more convenient for the implementation or nothing better + * is known + */ +#define AVINDEX_KEYFRAME 0x0001 +#define AVINDEX_DISCARD_FRAME 0x0002 /** + * Flag is used to indicate which frame should be discarded after decoding. + */ + int flags:2; + int size:30; //Yeah, trying to keep the size of this small to reduce memory requirements (it is 24 vs. 32 bytes due to possible 8-byte alignment). + int min_distance; /**< Minimum distance between this and the previous keyframe, used to avoid unneeded searching. */ +} AVIndexEntry; + +#define AV_DISPOSITION_DEFAULT 0x0001 +#define AV_DISPOSITION_DUB 0x0002 +#define AV_DISPOSITION_ORIGINAL 0x0004 +#define AV_DISPOSITION_COMMENT 0x0008 +#define AV_DISPOSITION_LYRICS 0x0010 +#define AV_DISPOSITION_KARAOKE 0x0020 + +/** + * Track should be used during playback by default. + * Useful for subtitle track that should be displayed + * even when user did not explicitly ask for subtitles. + */ +#define AV_DISPOSITION_FORCED 0x0040 +#define AV_DISPOSITION_HEARING_IMPAIRED 0x0080 /**< stream for hearing impaired audiences */ +#define AV_DISPOSITION_VISUAL_IMPAIRED 0x0100 /**< stream for visual impaired audiences */ +#define AV_DISPOSITION_CLEAN_EFFECTS 0x0200 /**< stream without voice */ +/** + * The stream is stored in the file as an attached picture/"cover art" (e.g. + * APIC frame in ID3v2). The first (usually only) packet associated with it + * will be returned among the first few packets read from the file unless + * seeking takes place. It can also be accessed at any time in + * AVStream.attached_pic. + */ +#define AV_DISPOSITION_ATTACHED_PIC 0x0400 +/** + * The stream is sparse, and contains thumbnail images, often corresponding + * to chapter markers. Only ever used with AV_DISPOSITION_ATTACHED_PIC. + */ +#define AV_DISPOSITION_TIMED_THUMBNAILS 0x0800 + +typedef struct AVStreamInternal AVStreamInternal; + +/** + * To specify text track kind (different from subtitles default). + */ +#define AV_DISPOSITION_CAPTIONS 0x10000 +#define AV_DISPOSITION_DESCRIPTIONS 0x20000 +#define AV_DISPOSITION_METADATA 0x40000 +#define AV_DISPOSITION_DEPENDENT 0x80000 ///< dependent audio stream (mix_type=0 in mpegts) +#define AV_DISPOSITION_STILL_IMAGE 0x100000 ///< still images in video stream (still_picture_flag=1 in mpegts) + +/** + * Options for behavior on timestamp wrap detection. + */ +#define AV_PTS_WRAP_IGNORE 0 ///< ignore the wrap +#define AV_PTS_WRAP_ADD_OFFSET 1 ///< add the format specific offset on wrap detection +#define AV_PTS_WRAP_SUB_OFFSET -1 ///< subtract the format specific offset on wrap detection + +/** + * Stream structure. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVStream) must not be used outside libav*. + */ +typedef struct AVStream { + int index; /**< stream index in AVFormatContext */ + /** + * Format-specific stream ID. + * decoding: set by libavformat + * encoding: set by the user, replaced by libavformat if left unset + */ + int id; +#if FF_API_LAVF_AVCTX + /** + * @deprecated use the codecpar struct instead + */ + attribute_deprecated + AVCodecContext *codec; +#endif + void *priv_data; + + /** + * This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. + * + * decoding: set by libavformat + * encoding: May be set by the caller before avformat_write_header() to + * provide a hint to the muxer about the desired timebase. In + * avformat_write_header(), the muxer will overwrite this field + * with the timebase that will actually be used for the timestamps + * written into the file (which may or may not be related to the + * user-provided one, depending on the format). + */ + AVRational time_base; + + /** + * Decoding: pts of the first frame of the stream in presentation order, in stream time base. + * Only set this if you are absolutely 100% sure that the value you set + * it to really is the pts of the first frame. + * This may be undefined (AV_NOPTS_VALUE). + * @note The ASF header does NOT contain a correct start_time the ASF + * demuxer must NOT set this. + */ + int64_t start_time; + + /** + * Decoding: duration of the stream, in stream time base. + * If a source file does not specify a duration, but does specify + * a bitrate, this value will be estimated from bitrate and file size. + * + * Encoding: May be set by the caller before avformat_write_header() to + * provide a hint to the muxer about the estimated duration. + */ + int64_t duration; + + int64_t nb_frames; ///< number of frames in this stream if known or 0 + + int disposition; /**< AV_DISPOSITION_* bit field */ + + enum AVDiscard discard; ///< Selects which packets can be discarded at will and do not need to be demuxed. + + /** + * sample aspect ratio (0 if unknown) + * - encoding: Set by user. + * - decoding: Set by libavformat. + */ + AVRational sample_aspect_ratio; + + AVDictionary *metadata; + + /** + * Average framerate + * + * - demuxing: May be set by libavformat when creating the stream or in + * avformat_find_stream_info(). + * - muxing: May be set by the caller before avformat_write_header(). + */ + AVRational avg_frame_rate; + + /** + * For streams with AV_DISPOSITION_ATTACHED_PIC disposition, this packet + * will contain the attached picture. + * + * decoding: set by libavformat, must not be modified by the caller. + * encoding: unused + */ + AVPacket attached_pic; + + /** + * An array of side data that applies to the whole stream (i.e. the + * container does not allow it to change between packets). + * + * There may be no overlap between the side data in this array and side data + * in the packets. I.e. a given side data is either exported by the muxer + * (demuxing) / set by the caller (muxing) in this array, then it never + * appears in the packets, or the side data is exported / sent through + * the packets (always in the first packet where the value becomes known or + * changes), then it does not appear in this array. + * + * - demuxing: Set by libavformat when the stream is created. + * - muxing: May be set by the caller before avformat_write_header(). + * + * Freed by libavformat in avformat_free_context(). + * + * @see av_format_inject_global_side_data() + */ + AVPacketSideData *side_data; + /** + * The number of elements in the AVStream.side_data array. + */ + int nb_side_data; + + /** + * Flags for the user to detect events happening on the stream. Flags must + * be cleared by the user once the event has been handled. + * A combination of AVSTREAM_EVENT_FLAG_*. + */ + int event_flags; +#define AVSTREAM_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata. + + /** + * Real base framerate of the stream. + * This is the lowest framerate with which all timestamps can be + * represented accurately (it is the least common multiple of all + * framerates in the stream). Note, this value is just a guess! + * For example, if the time base is 1/90000 and all frames have either + * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1. + */ + AVRational r_frame_rate; + +#if FF_API_LAVF_FFSERVER + /** + * String containing pairs of key and values describing recommended encoder configuration. + * Pairs are separated by ','. + * Keys are separated from values by '='. + * + * @deprecated unused + */ + attribute_deprecated + char *recommended_encoder_configuration; +#endif + + /** + * Codec parameters associated with this stream. Allocated and freed by + * libavformat in avformat_new_stream() and avformat_free_context() + * respectively. + * + * - demuxing: filled by libavformat on stream creation or in + * avformat_find_stream_info() + * - muxing: filled by the caller before avformat_write_header() + */ + AVCodecParameters *codecpar; + + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * Internal note: be aware that physically removing these fields + * will break ABI. Replace removed fields with dummy fields, and + * add new fields to AVStreamInternal. + ***************************************************************** + */ + +#define MAX_STD_TIMEBASES (30*12+30+3+6) + /** + * Stream information used internally by avformat_find_stream_info() + */ + struct { + int64_t last_dts; + int64_t duration_gcd; + int duration_count; + int64_t rfps_duration_sum; + double (*duration_error)[2][MAX_STD_TIMEBASES]; + int64_t codec_info_duration; + int64_t codec_info_duration_fields; + int frame_delay_evidence; + + /** + * 0 -> decoder has not been searched for yet. + * >0 -> decoder found + * <0 -> decoder with codec_id == -found_decoder has not been found + */ + int found_decoder; + + int64_t last_duration; + + /** + * Those are used for average framerate estimation. + */ + int64_t fps_first_dts; + int fps_first_dts_idx; + int64_t fps_last_dts; + int fps_last_dts_idx; + + } *info; + + int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */ + + // Timestamp generation support: + /** + * Timestamp corresponding to the last dts sync point. + * + * Initialized when AVCodecParserContext.dts_sync_point >= 0 and + * a DTS is received from the underlying container. Otherwise set to + * AV_NOPTS_VALUE by default. + */ + int64_t first_dts; + int64_t cur_dts; + int64_t last_IP_pts; + int last_IP_duration; + + /** + * Number of packets to buffer for codec probing + */ + int probe_packets; + + /** + * Number of frames that have been demuxed during avformat_find_stream_info() + */ + int codec_info_nb_frames; + + /* av_read_frame() support */ + enum AVStreamParseType need_parsing; + struct AVCodecParserContext *parser; + + /** + * last packet in packet_buffer for this stream when muxing. + */ + struct AVPacketList *last_in_packet_buffer; + AVProbeData probe_data; +#define MAX_REORDER_DELAY 16 + int64_t pts_buffer[MAX_REORDER_DELAY+1]; + + AVIndexEntry *index_entries; /**< Only used if the format does not + support seeking natively. */ + int nb_index_entries; + unsigned int index_entries_allocated_size; + + /** + * Stream Identifier + * This is the MPEG-TS stream identifier +1 + * 0 means unknown + */ + int stream_identifier; + + /** + * Details of the MPEG-TS program which created this stream. + */ + int program_num; + int pmt_version; + int pmt_stream_idx; + + int64_t interleaver_chunk_size; + int64_t interleaver_chunk_duration; + + /** + * stream probing state + * -1 -> probing finished + * 0 -> no probing requested + * rest -> perform probing with request_probe being the minimum score to accept. + * NOT PART OF PUBLIC API + */ + int request_probe; + /** + * Indicates that everything up to the next keyframe + * should be discarded. + */ + int skip_to_keyframe; + + /** + * Number of samples to skip at the start of the frame decoded from the next packet. + */ + int skip_samples; + + /** + * If not 0, the number of samples that should be skipped from the start of + * the stream (the samples are removed from packets with pts==0, which also + * assumes negative timestamps do not happen). + * Intended for use with formats such as mp3 with ad-hoc gapless audio + * support. + */ + int64_t start_skip_samples; + + /** + * If not 0, the first audio sample that should be discarded from the stream. + * This is broken by design (needs global sample count), but can't be + * avoided for broken by design formats such as mp3 with ad-hoc gapless + * audio support. + */ + int64_t first_discard_sample; + + /** + * The sample after last sample that is intended to be discarded after + * first_discard_sample. Works on frame boundaries only. Used to prevent + * early EOF if the gapless info is broken (considered concatenated mp3s). + */ + int64_t last_discard_sample; + + /** + * Number of internally decoded frames, used internally in libavformat, do not access + * its lifetime differs from info which is why it is not in that structure. + */ + int nb_decoded_frames; + + /** + * Timestamp offset added to timestamps before muxing + * NOT PART OF PUBLIC API + */ + int64_t mux_ts_offset; + + /** + * Internal data to check for wrapping of the time stamp + */ + int64_t pts_wrap_reference; + + /** + * Options for behavior, when a wrap is detected. + * + * Defined by AV_PTS_WRAP_ values. + * + * If correction is enabled, there are two possibilities: + * If the first time stamp is near the wrap point, the wrap offset + * will be subtracted, which will create negative time stamps. + * Otherwise the offset will be added. + */ + int pts_wrap_behavior; + + /** + * Internal data to prevent doing update_initial_durations() twice + */ + int update_initial_durations_done; + + /** + * Internal data to generate dts from pts + */ + int64_t pts_reorder_error[MAX_REORDER_DELAY+1]; + uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1]; + + /** + * Internal data to analyze DTS and detect faulty mpeg streams + */ + int64_t last_dts_for_order_check; + uint8_t dts_ordered; + uint8_t dts_misordered; + + /** + * Internal data to inject global side data + */ + int inject_global_side_data; + + /** + * display aspect ratio (0 if unknown) + * - encoding: unused + * - decoding: Set by libavformat to calculate sample_aspect_ratio internally + */ + AVRational display_aspect_ratio; + + /** + * An opaque field for libavformat internal usage. + * Must not be accessed in any way by callers. + */ + AVStreamInternal *internal; +} AVStream; + +#if FF_API_FORMAT_GET_SET +/** + * Accessors for some AVStream fields. These used to be provided for ABI + * compatibility, and do not need to be used anymore. + */ +attribute_deprecated +AVRational av_stream_get_r_frame_rate(const AVStream *s); +attribute_deprecated +void av_stream_set_r_frame_rate(AVStream *s, AVRational r); +#if FF_API_LAVF_FFSERVER +attribute_deprecated +char* av_stream_get_recommended_encoder_configuration(const AVStream *s); +attribute_deprecated +void av_stream_set_recommended_encoder_configuration(AVStream *s, char *configuration); +#endif +#endif + +struct AVCodecParserContext *av_stream_get_parser(const AVStream *s); + +/** + * Returns the pts of the last muxed packet + its duration + * + * the retuned value is undefined when used with a demuxer. + */ +int64_t av_stream_get_end_pts(const AVStream *st); + +#define AV_PROGRAM_RUNNING 1 + +/** + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVProgram) must not be used outside libav*. + */ +typedef struct AVProgram { + int id; + int flags; + enum AVDiscard discard; ///< selects which program to discard and which to feed to the caller + unsigned int *stream_index; + unsigned int nb_stream_indexes; + AVDictionary *metadata; + + int program_num; + int pmt_pid; + int pcr_pid; + int pmt_version; + + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + int64_t start_time; + int64_t end_time; + + int64_t pts_wrap_reference; ///< reference dts for wrap detection + int pts_wrap_behavior; ///< behavior on wrap detection +} AVProgram; + +#define AVFMTCTX_NOHEADER 0x0001 /**< signal that no header is present + (streams are added dynamically) */ +#define AVFMTCTX_UNSEEKABLE 0x0002 /**< signal that the stream is definitely + not seekable, and attempts to call the + seek function will fail. For some + network protocols (e.g. HLS), this can + change dynamically at runtime. */ + +typedef struct AVChapter { + int id; ///< unique ID to identify the chapter + AVRational time_base; ///< time base in which the start/end timestamps are specified + int64_t start, end; ///< chapter start/end time in time_base units + AVDictionary *metadata; +} AVChapter; + + +/** + * Callback used by devices to communicate with application. + */ +typedef int (*av_format_control_message)(struct AVFormatContext *s, int type, + void *data, size_t data_size); + +typedef int (*AVOpenCallback)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options); + +/** + * The duration of a video can be estimated through various ways, and this enum can be used + * to know how the duration was estimated. + */ +enum AVDurationEstimationMethod { + AVFMT_DURATION_FROM_PTS, ///< Duration accurately estimated from PTSes + AVFMT_DURATION_FROM_STREAM, ///< Duration estimated from a stream with a known duration + AVFMT_DURATION_FROM_BITRATE ///< Duration estimated from bitrate (less accurate) +}; + +typedef struct AVFormatInternal AVFormatInternal; + +/** + * Format I/O context. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVFormatContext) must not be used outside libav*, use + * avformat_alloc_context() to create an AVFormatContext. + * + * Fields can be accessed through AVOptions (av_opt*), + * the name string used matches the associated command line parameter name and + * can be found in libavformat/options_table.h. + * The AVOption/command line parameter names differ in some cases from the C + * structure field names for historic reasons or brevity. + */ +typedef struct AVFormatContext { + /** + * A class for logging and @ref avoptions. Set by avformat_alloc_context(). + * Exports (de)muxer private options if they exist. + */ + const AVClass *av_class; + + /** + * The input container format. + * + * Demuxing only, set by avformat_open_input(). + */ + struct AVInputFormat *iformat; + + /** + * The output container format. + * + * Muxing only, must be set by the caller before avformat_write_header(). + */ + struct AVOutputFormat *oformat; + + /** + * Format private data. This is an AVOptions-enabled struct + * if and only if iformat/oformat.priv_class is not NULL. + * + * - muxing: set by avformat_write_header() + * - demuxing: set by avformat_open_input() + */ + void *priv_data; + + /** + * I/O context. + * + * - demuxing: either set by the user before avformat_open_input() (then + * the user must close it manually) or set by avformat_open_input(). + * - muxing: set by the user before avformat_write_header(). The caller must + * take care of closing / freeing the IO context. + * + * Do NOT set this field if AVFMT_NOFILE flag is set in + * iformat/oformat.flags. In such a case, the (de)muxer will handle + * I/O in some other way and this field will be NULL. + */ + AVIOContext *pb; + + /* stream info */ + /** + * Flags signalling stream properties. A combination of AVFMTCTX_*. + * Set by libavformat. + */ + int ctx_flags; + + /** + * Number of elements in AVFormatContext.streams. + * + * Set by avformat_new_stream(), must not be modified by any other code. + */ + unsigned int nb_streams; + /** + * A list of all streams in the file. New streams are created with + * avformat_new_stream(). + * + * - demuxing: streams are created by libavformat in avformat_open_input(). + * If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also + * appear in av_read_frame(). + * - muxing: streams are created by the user before avformat_write_header(). + * + * Freed by libavformat in avformat_free_context(). + */ + AVStream **streams; + +#if FF_API_FORMAT_FILENAME + /** + * input or output filename + * + * - demuxing: set by avformat_open_input() + * - muxing: may be set by the caller before avformat_write_header() + * + * @deprecated Use url instead. + */ + attribute_deprecated + char filename[1024]; +#endif + + /** + * input or output URL. Unlike the old filename field, this field has no + * length restriction. + * + * - demuxing: set by avformat_open_input(), initialized to an empty + * string if url parameter was NULL in avformat_open_input(). + * - muxing: may be set by the caller before calling avformat_write_header() + * (or avformat_init_output() if that is called first) to a string + * which is freeable by av_free(). Set to an empty string if it + * was NULL in avformat_init_output(). + * + * Freed by libavformat in avformat_free_context(). + */ + char *url; + + /** + * Position of the first frame of the component, in + * AV_TIME_BASE fractional seconds. NEVER set this value directly: + * It is deduced from the AVStream values. + * + * Demuxing only, set by libavformat. + */ + int64_t start_time; + + /** + * Duration of the stream, in AV_TIME_BASE fractional + * seconds. Only set this value if you know none of the individual stream + * durations and also do not set any of them. This is deduced from the + * AVStream values if not set. + * + * Demuxing only, set by libavformat. + */ + int64_t duration; + + /** + * Total stream bitrate in bit/s, 0 if not + * available. Never set it directly if the file_size and the + * duration are known as FFmpeg can compute it automatically. + */ + int64_t bit_rate; + + unsigned int packet_size; + int max_delay; + + /** + * Flags modifying the (de)muxer behaviour. A combination of AVFMT_FLAG_*. + * Set by the user before avformat_open_input() / avformat_write_header(). + */ + int flags; +#define AVFMT_FLAG_GENPTS 0x0001 ///< Generate missing pts even if it requires parsing future frames. +#define AVFMT_FLAG_IGNIDX 0x0002 ///< Ignore index. +#define AVFMT_FLAG_NONBLOCK 0x0004 ///< Do not block when reading packets from input. +#define AVFMT_FLAG_IGNDTS 0x0008 ///< Ignore DTS on frames that contain both DTS & PTS +#define AVFMT_FLAG_NOFILLIN 0x0010 ///< Do not infer any values from other values, just return what is stored in the container +#define AVFMT_FLAG_NOPARSE 0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled +#define AVFMT_FLAG_NOBUFFER 0x0040 ///< Do not buffer frames when possible +#define AVFMT_FLAG_CUSTOM_IO 0x0080 ///< The caller has supplied a custom AVIOContext, don't avio_close() it. +#define AVFMT_FLAG_DISCARD_CORRUPT 0x0100 ///< Discard frames marked corrupted +#define AVFMT_FLAG_FLUSH_PACKETS 0x0200 ///< Flush the AVIOContext every packet. +/** + * When muxing, try to avoid writing any random/volatile data to the output. + * This includes any random IDs, real-time timestamps/dates, muxer version, etc. + * + * This flag is mainly intended for testing. + */ +#define AVFMT_FLAG_BITEXACT 0x0400 +#if FF_API_LAVF_MP4A_LATM +#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Deprecated, does nothing. +#endif +#define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down) +#define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted) +#if FF_API_LAVF_KEEPSIDE_FLAG +#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Deprecated, does nothing. +#endif +#define AVFMT_FLAG_FAST_SEEK 0x80000 ///< Enable fast, but inaccurate seeks for some formats +#define AVFMT_FLAG_SHORTEST 0x100000 ///< Stop muxing when the shortest stream stops. +#define AVFMT_FLAG_AUTO_BSF 0x200000 ///< Add bitstream filters as requested by the muxer + + /** + * Maximum size of the data read from input for determining + * the input container format. + * Demuxing only, set by the caller before avformat_open_input(). + */ + int64_t probesize; + + /** + * Maximum duration (in AV_TIME_BASE units) of the data read + * from input in avformat_find_stream_info(). + * Demuxing only, set by the caller before avformat_find_stream_info(). + * Can be set to 0 to let avformat choose using a heuristic. + */ + int64_t max_analyze_duration; + + const uint8_t *key; + int keylen; + + unsigned int nb_programs; + AVProgram **programs; + + /** + * Forced video codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID video_codec_id; + + /** + * Forced audio codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID audio_codec_id; + + /** + * Forced subtitle codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID subtitle_codec_id; + + /** + * Maximum amount of memory in bytes to use for the index of each stream. + * If the index exceeds this size, entries will be discarded as + * needed to maintain a smaller size. This can lead to slower or less + * accurate seeking (depends on demuxer). + * Demuxers for which a full in-memory index is mandatory will ignore + * this. + * - muxing: unused + * - demuxing: set by user + */ + unsigned int max_index_size; + + /** + * Maximum amount of memory in bytes to use for buffering frames + * obtained from realtime capture devices. + */ + unsigned int max_picture_buffer; + + /** + * Number of chapters in AVChapter array. + * When muxing, chapters are normally written in the file header, + * so nb_chapters should normally be initialized before write_header + * is called. Some muxers (e.g. mov and mkv) can also write chapters + * in the trailer. To write chapters in the trailer, nb_chapters + * must be zero when write_header is called and non-zero when + * write_trailer is called. + * - muxing: set by user + * - demuxing: set by libavformat + */ + unsigned int nb_chapters; + AVChapter **chapters; + + /** + * Metadata that applies to the whole file. + * + * - demuxing: set by libavformat in avformat_open_input() + * - muxing: may be set by the caller before avformat_write_header() + * + * Freed by libavformat in avformat_free_context(). + */ + AVDictionary *metadata; + + /** + * Start time of the stream in real world time, in microseconds + * since the Unix epoch (00:00 1st January 1970). That is, pts=0 in the + * stream was captured at this real world time. + * - muxing: Set by the caller before avformat_write_header(). If set to + * either 0 or AV_NOPTS_VALUE, then the current wall-time will + * be used. + * - demuxing: Set by libavformat. AV_NOPTS_VALUE if unknown. Note that + * the value may become known after some number of frames + * have been received. + */ + int64_t start_time_realtime; + + /** + * The number of frames used for determining the framerate in + * avformat_find_stream_info(). + * Demuxing only, set by the caller before avformat_find_stream_info(). + */ + int fps_probe_size; + + /** + * Error recognition; higher values will detect more errors but may + * misdetect some more or less valid parts as errors. + * Demuxing only, set by the caller before avformat_open_input(). + */ + int error_recognition; + + /** + * Custom interrupt callbacks for the I/O layer. + * + * demuxing: set by the user before avformat_open_input(). + * muxing: set by the user before avformat_write_header() + * (mainly useful for AVFMT_NOFILE formats). The callback + * should also be passed to avio_open2() if it's used to + * open the file. + */ + AVIOInterruptCB interrupt_callback; + + /** + * Flags to enable debugging. + */ + int debug; +#define FF_FDEBUG_TS 0x0001 + + /** + * Maximum buffering duration for interleaving. + * + * To ensure all the streams are interleaved correctly, + * av_interleaved_write_frame() will wait until it has at least one packet + * for each stream before actually writing any packets to the output file. + * When some streams are "sparse" (i.e. there are large gaps between + * successive packets), this can result in excessive buffering. + * + * This field specifies the maximum difference between the timestamps of the + * first and the last packet in the muxing queue, above which libavformat + * will output a packet regardless of whether it has queued a packet for all + * the streams. + * + * Muxing only, set by the caller before avformat_write_header(). + */ + int64_t max_interleave_delta; + + /** + * Allow non-standard and experimental extension + * @see AVCodecContext.strict_std_compliance + */ + int strict_std_compliance; + + /** + * Flags for the user to detect events happening on the file. Flags must + * be cleared by the user once the event has been handled. + * A combination of AVFMT_EVENT_FLAG_*. + */ + int event_flags; +#define AVFMT_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata. + + /** + * Maximum number of packets to read while waiting for the first timestamp. + * Decoding only. + */ + int max_ts_probe; + + /** + * Avoid negative timestamps during muxing. + * Any value of the AVFMT_AVOID_NEG_TS_* constants. + * Note, this only works when using av_interleaved_write_frame. (interleave_packet_per_dts is in use) + * - muxing: Set by user + * - demuxing: unused + */ + int avoid_negative_ts; +#define AVFMT_AVOID_NEG_TS_AUTO -1 ///< Enabled when required by target format +#define AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE 1 ///< Shift timestamps so they are non negative +#define AVFMT_AVOID_NEG_TS_MAKE_ZERO 2 ///< Shift timestamps so that they start at 0 + + /** + * Transport stream id. + * This will be moved into demuxer private options. Thus no API/ABI compatibility + */ + int ts_id; + + /** + * Audio preload in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user + * - decoding: unused + */ + int audio_preload; + + /** + * Max chunk time in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user + * - decoding: unused + */ + int max_chunk_duration; + + /** + * Max chunk size in bytes + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user + * - decoding: unused + */ + int max_chunk_size; + + /** + * forces the use of wallclock timestamps as pts/dts of packets + * This has undefined results in the presence of B frames. + * - encoding: unused + * - decoding: Set by user + */ + int use_wallclock_as_timestamps; + + /** + * avio flags, used to force AVIO_FLAG_DIRECT. + * - encoding: unused + * - decoding: Set by user + */ + int avio_flags; + + /** + * The duration field can be estimated through various ways, and this field can be used + * to know how the duration was estimated. + * - encoding: unused + * - decoding: Read by user + */ + enum AVDurationEstimationMethod duration_estimation_method; + + /** + * Skip initial bytes when opening stream + * - encoding: unused + * - decoding: Set by user + */ + int64_t skip_initial_bytes; + + /** + * Correct single timestamp overflows + * - encoding: unused + * - decoding: Set by user + */ + unsigned int correct_ts_overflow; + + /** + * Force seeking to any (also non key) frames. + * - encoding: unused + * - decoding: Set by user + */ + int seek2any; + + /** + * Flush the I/O context after each packet. + * - encoding: Set by user + * - decoding: unused + */ + int flush_packets; + + /** + * format probing score. + * The maximal score is AVPROBE_SCORE_MAX, its set when the demuxer probes + * the format. + * - encoding: unused + * - decoding: set by avformat, read by user + */ + int probe_score; + + /** + * number of bytes to read maximally to identify format. + * - encoding: unused + * - decoding: set by user + */ + int format_probesize; + + /** + * ',' separated list of allowed decoders. + * If NULL then all are allowed + * - encoding: unused + * - decoding: set by user + */ + char *codec_whitelist; + + /** + * ',' separated list of allowed demuxers. + * If NULL then all are allowed + * - encoding: unused + * - decoding: set by user + */ + char *format_whitelist; + + /** + * An opaque field for libavformat internal usage. + * Must not be accessed in any way by callers. + */ + AVFormatInternal *internal; + + /** + * IO repositioned flag. + * This is set by avformat when the underlaying IO context read pointer + * is repositioned, for example when doing byte based seeking. + * Demuxers can use the flag to detect such changes. + */ + int io_repositioned; + + /** + * Forced video codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user + */ + AVCodec *video_codec; + + /** + * Forced audio codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user + */ + AVCodec *audio_codec; + + /** + * Forced subtitle codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user + */ + AVCodec *subtitle_codec; + + /** + * Forced data codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user + */ + AVCodec *data_codec; + + /** + * Number of bytes to be written as padding in a metadata header. + * Demuxing: Unused. + * Muxing: Set by user via av_format_set_metadata_header_padding. + */ + int metadata_header_padding; + + /** + * User data. + * This is a place for some private data of the user. + */ + void *opaque; + + /** + * Callback used by devices to communicate with application. + */ + av_format_control_message control_message_cb; + + /** + * Output timestamp offset, in microseconds. + * Muxing: set by user + */ + int64_t output_ts_offset; + + /** + * dump format separator. + * can be ", " or "\n " or anything else + * - muxing: Set by user. + * - demuxing: Set by user. + */ + uint8_t *dump_separator; + + /** + * Forced Data codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID data_codec_id; + +#if FF_API_OLD_OPEN_CALLBACKS + /** + * Called to open further IO contexts when needed for demuxing. + * + * This can be set by the user application to perform security checks on + * the URLs before opening them. + * The function should behave like avio_open2(), AVFormatContext is provided + * as contextual information and to reach AVFormatContext.opaque. + * + * If NULL then some simple checks are used together with avio_open2(). + * + * Must not be accessed directly from outside avformat. + * @See av_format_set_open_cb() + * + * Demuxing: Set by user. + * + * @deprecated Use io_open and io_close. + */ + attribute_deprecated + int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options); +#endif + + /** + * ',' separated list of allowed protocols. + * - encoding: unused + * - decoding: set by user + */ + char *protocol_whitelist; + + /** + * A callback for opening new IO streams. + * + * Whenever a muxer or a demuxer needs to open an IO stream (typically from + * avformat_open_input() for demuxers, but for certain formats can happen at + * other times as well), it will call this callback to obtain an IO context. + * + * @param s the format context + * @param pb on success, the newly opened IO context should be returned here + * @param url the url to open + * @param flags a combination of AVIO_FLAG_* + * @param options a dictionary of additional options, with the same + * semantics as in avio_open2() + * @return 0 on success, a negative AVERROR code on failure + * + * @note Certain muxers and demuxers do nesting, i.e. they open one or more + * additional internal format contexts. Thus the AVFormatContext pointer + * passed to this callback may be different from the one facing the caller. + * It will, however, have the same 'opaque' field. + */ + int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, + int flags, AVDictionary **options); + + /** + * A callback for closing the streams opened with AVFormatContext.io_open(). + */ + void (*io_close)(struct AVFormatContext *s, AVIOContext *pb); + + /** + * ',' separated list of disallowed protocols. + * - encoding: unused + * - decoding: set by user + */ + char *protocol_blacklist; + + /** + * The maximum number of streams. + * - encoding: unused + * - decoding: set by user + */ + int max_streams; + + /** + * Skip duration calcuation in estimate_timings_from_pts. + * - encoding: unused + * - decoding: set by user + */ + int skip_estimate_duration_from_pts; +} AVFormatContext; + +#if FF_API_FORMAT_GET_SET +/** + * Accessors for some AVFormatContext fields. These used to be provided for ABI + * compatibility, and do not need to be used anymore. + */ +attribute_deprecated +int av_format_get_probe_score(const AVFormatContext *s); +attribute_deprecated +AVCodec * av_format_get_video_codec(const AVFormatContext *s); +attribute_deprecated +void av_format_set_video_codec(AVFormatContext *s, AVCodec *c); +attribute_deprecated +AVCodec * av_format_get_audio_codec(const AVFormatContext *s); +attribute_deprecated +void av_format_set_audio_codec(AVFormatContext *s, AVCodec *c); +attribute_deprecated +AVCodec * av_format_get_subtitle_codec(const AVFormatContext *s); +attribute_deprecated +void av_format_set_subtitle_codec(AVFormatContext *s, AVCodec *c); +attribute_deprecated +AVCodec * av_format_get_data_codec(const AVFormatContext *s); +attribute_deprecated +void av_format_set_data_codec(AVFormatContext *s, AVCodec *c); +attribute_deprecated +int av_format_get_metadata_header_padding(const AVFormatContext *s); +attribute_deprecated +void av_format_set_metadata_header_padding(AVFormatContext *s, int c); +attribute_deprecated +void * av_format_get_opaque(const AVFormatContext *s); +attribute_deprecated +void av_format_set_opaque(AVFormatContext *s, void *opaque); +attribute_deprecated +av_format_control_message av_format_get_control_message_cb(const AVFormatContext *s); +attribute_deprecated +void av_format_set_control_message_cb(AVFormatContext *s, av_format_control_message callback); +#if FF_API_OLD_OPEN_CALLBACKS +attribute_deprecated AVOpenCallback av_format_get_open_cb(const AVFormatContext *s); +attribute_deprecated void av_format_set_open_cb(AVFormatContext *s, AVOpenCallback callback); +#endif +#endif + +/** + * This function will cause global side data to be injected in the next packet + * of each stream as well as after any subsequent seek. + */ +void av_format_inject_global_side_data(AVFormatContext *s); + +/** + * Returns the method used to set ctx->duration. + * + * @return AVFMT_DURATION_FROM_PTS, AVFMT_DURATION_FROM_STREAM, or AVFMT_DURATION_FROM_BITRATE. + */ +enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx); + +typedef struct AVPacketList { + AVPacket pkt; + struct AVPacketList *next; +} AVPacketList; + + +/** + * @defgroup lavf_core Core functions + * @ingroup libavf + * + * Functions for querying libavformat capabilities, allocating core structures, + * etc. + * @{ + */ + +/** + * Return the LIBAVFORMAT_VERSION_INT constant. + */ +unsigned avformat_version(void); + +/** + * Return the libavformat build-time configuration. + */ +const char *avformat_configuration(void); + +/** + * Return the libavformat license. + */ +const char *avformat_license(void); + +#if FF_API_NEXT +/** + * Initialize libavformat and register all the muxers, demuxers and + * protocols. If you do not call this function, then you can select + * exactly which formats you want to support. + * + * @see av_register_input_format() + * @see av_register_output_format() + */ +attribute_deprecated +void av_register_all(void); + +attribute_deprecated +void av_register_input_format(AVInputFormat *format); +attribute_deprecated +void av_register_output_format(AVOutputFormat *format); +#endif + +/** + * Do global initialization of network libraries. This is optional, + * and not recommended anymore. + * + * This functions only exists to work around thread-safety issues + * with older GnuTLS or OpenSSL libraries. If libavformat is linked + * to newer versions of those libraries, or if you do not use them, + * calling this function is unnecessary. Otherwise, you need to call + * this function before any other threads using them are started. + * + * This function will be deprecated once support for older GnuTLS and + * OpenSSL libraries is removed, and this function has no purpose + * anymore. + */ +int avformat_network_init(void); + +/** + * Undo the initialization done by avformat_network_init. Call it only + * once for each time you called avformat_network_init. + */ +int avformat_network_deinit(void); + +#if FF_API_NEXT +/** + * If f is NULL, returns the first registered input format, + * if f is non-NULL, returns the next registered input format after f + * or NULL if f is the last one. + */ +attribute_deprecated +AVInputFormat *av_iformat_next(const AVInputFormat *f); + +/** + * If f is NULL, returns the first registered output format, + * if f is non-NULL, returns the next registered output format after f + * or NULL if f is the last one. + */ +attribute_deprecated +AVOutputFormat *av_oformat_next(const AVOutputFormat *f); +#endif + +/** + * Iterate over all registered muxers. + * + * @param opaque a pointer where libavformat will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered muxer or NULL when the iteration is + * finished + */ +const AVOutputFormat *av_muxer_iterate(void **opaque); + +/** + * Iterate over all registered demuxers. + * + * @param opaque a pointer where libavformat will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered demuxer or NULL when the iteration is + * finished + */ +const AVInputFormat *av_demuxer_iterate(void **opaque); + +/** + * Allocate an AVFormatContext. + * avformat_free_context() can be used to free the context and everything + * allocated by the framework within it. + */ +AVFormatContext *avformat_alloc_context(void); + +/** + * Free an AVFormatContext and all its streams. + * @param s context to free + */ +void avformat_free_context(AVFormatContext *s); + +/** + * Get the AVClass for AVFormatContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *avformat_get_class(void); + +/** + * Add a new stream to a media file. + * + * When demuxing, it is called by the demuxer in read_header(). If the + * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also + * be called in read_packet(). + * + * When muxing, should be called by the user before avformat_write_header(). + * + * User is required to call avcodec_close() and avformat_free_context() to + * clean up the allocation by avformat_new_stream(). + * + * @param s media file handle + * @param c If non-NULL, the AVCodecContext corresponding to the new stream + * will be initialized to use this codec. This is needed for e.g. codec-specific + * defaults to be set, so codec should be provided if it is known. + * + * @return newly created stream or NULL on error. + */ +AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c); + +/** + * Wrap an existing array as stream side data. + * + * @param st stream + * @param type side information type + * @param data the side data array. It must be allocated with the av_malloc() + * family of functions. The ownership of the data is transferred to + * st. + * @param size side information size + * @return zero on success, a negative AVERROR code on failure. On failure, + * the stream is unchanged and the data remains owned by the caller. + */ +int av_stream_add_side_data(AVStream *st, enum AVPacketSideDataType type, + uint8_t *data, size_t size); + +/** + * Allocate new information from stream. + * + * @param stream stream + * @param type desired side information type + * @param size side information size + * @return pointer to fresh allocated data or NULL otherwise + */ +uint8_t *av_stream_new_side_data(AVStream *stream, + enum AVPacketSideDataType type, int size); +/** + * Get side information from stream. + * + * @param stream stream + * @param type desired side information type + * @param size pointer for side information size to store (optional) + * @return pointer to data if present or NULL otherwise + */ +uint8_t *av_stream_get_side_data(const AVStream *stream, + enum AVPacketSideDataType type, int *size); + +AVProgram *av_new_program(AVFormatContext *s, int id); + +/** + * @} + */ + + +/** + * Allocate an AVFormatContext for an output format. + * avformat_free_context() can be used to free the context and + * everything allocated by the framework within it. + * + * @param *ctx is set to the created format context, or to NULL in + * case of failure + * @param oformat format to use for allocating the context, if NULL + * format_name and filename are used instead + * @param format_name the name of output format to use for allocating the + * context, if NULL filename is used instead + * @param filename the name of the filename to use for allocating the + * context, may be NULL + * @return >= 0 in case of success, a negative AVERROR code in case of + * failure + */ +int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, + const char *format_name, const char *filename); + +/** + * @addtogroup lavf_decoding + * @{ + */ + +/** + * Find AVInputFormat based on the short name of the input format. + */ +AVInputFormat *av_find_input_format(const char *short_name); + +/** + * Guess the file format. + * + * @param pd data to be probed + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + */ +AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened); + +/** + * Guess the file format. + * + * @param pd data to be probed + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + * @param score_max A probe score larger that this is required to accept a + * detection, the variable is set to the actual detection + * score afterwards. + * If the score is <= AVPROBE_SCORE_MAX / 4 it is recommended + * to retry with a larger probe buffer. + */ +AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max); + +/** + * Guess the file format. + * + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + * @param score_ret The score of the best detection. + */ +AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, int *score_ret); + +/** + * Probe a bytestream to determine the input format. Each time a probe returns + * with a score that is too low, the probe buffer size is increased and another + * attempt is made. When the maximum probe size is reached, the input format + * with the highest score is returned. + * + * @param pb the bytestream to probe + * @param fmt the input format is put here + * @param url the url of the stream + * @param logctx the log context + * @param offset the offset within the bytestream to probe from + * @param max_probe_size the maximum probe buffer size (zero for default) + * @return the score in case of success, a negative value corresponding to an + * the maximal score is AVPROBE_SCORE_MAX + * AVERROR code otherwise + */ +int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt, + const char *url, void *logctx, + unsigned int offset, unsigned int max_probe_size); + +/** + * Like av_probe_input_buffer2() but returns 0 on success + */ +int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, + const char *url, void *logctx, + unsigned int offset, unsigned int max_probe_size); + +/** + * Open an input stream and read the header. The codecs are not opened. + * The stream must be closed with avformat_close_input(). + * + * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context). + * May be a pointer to NULL, in which case an AVFormatContext is allocated by this + * function and written into ps. + * Note that a user-supplied AVFormatContext will be freed on failure. + * @param url URL of the stream to open. + * @param fmt If non-NULL, this parameter forces a specific input format. + * Otherwise the format is autodetected. + * @param options A dictionary filled with AVFormatContext and demuxer-private options. + * On return this parameter will be destroyed and replaced with a dict containing + * options that were not found. May be NULL. + * + * @return 0 on success, a negative AVERROR on failure. + * + * @note If you want to use custom IO, preallocate the format context and set its pb field. + */ +int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options); + +attribute_deprecated +int av_demuxer_open(AVFormatContext *ic); + +/** + * Read packets of a media file to get stream information. This + * is useful for file formats with no headers such as MPEG. This + * function also computes the real framerate in case of MPEG-2 repeat + * frame mode. + * The logical file position is not changed by this function; + * examined packets may be buffered for later processing. + * + * @param ic media file handle + * @param options If non-NULL, an ic.nb_streams long array of pointers to + * dictionaries, where i-th member contains options for + * codec corresponding to i-th stream. + * On return each dictionary will be filled with options that were not found. + * @return >=0 if OK, AVERROR_xxx on error + * + * @note this function isn't guaranteed to open all the codecs, so + * options being non-empty at return is a perfectly normal behavior. + * + * @todo Let the user decide somehow what information is needed so that + * we do not waste time getting stuff the user does not need. + */ +int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options); + +/** + * Find the programs which belong to a given stream. + * + * @param ic media file handle + * @param last the last found program, the search will start after this + * program, or from the beginning if it is NULL + * @param s stream index + * @return the next program which belongs to s, NULL if no program is found or + * the last program is not among the programs of ic. + */ +AVProgram *av_find_program_from_stream(AVFormatContext *ic, AVProgram *last, int s); + +void av_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int idx); + +/** + * Find the "best" stream in the file. + * The best stream is determined according to various heuristics as the most + * likely to be what the user expects. + * If the decoder parameter is non-NULL, av_find_best_stream will find the + * default decoder for the stream's codec; streams for which no decoder can + * be found are ignored. + * + * @param ic media file handle + * @param type stream type: video, audio, subtitles, etc. + * @param wanted_stream_nb user-requested stream number, + * or -1 for automatic selection + * @param related_stream try to find a stream related (eg. in the same + * program) to this one, or -1 if none + * @param decoder_ret if non-NULL, returns the decoder for the + * selected stream + * @param flags flags; none are currently defined + * @return the non-negative stream number in case of success, + * AVERROR_STREAM_NOT_FOUND if no stream with the requested type + * could be found, + * AVERROR_DECODER_NOT_FOUND if streams were found but no decoder + * @note If av_find_best_stream returns successfully and decoder_ret is not + * NULL, then *decoder_ret is guaranteed to be set to a valid AVCodec. + */ +int av_find_best_stream(AVFormatContext *ic, + enum AVMediaType type, + int wanted_stream_nb, + int related_stream, + AVCodec **decoder_ret, + int flags); + +/** + * Return the next frame of a stream. + * This function returns what is stored in the file, and does not validate + * that what is there are valid frames for the decoder. It will split what is + * stored in the file into frames and return one for each call. It will not + * omit invalid data between valid frames so as to give the decoder the maximum + * information possible for decoding. + * + * If pkt->buf is NULL, then the packet is valid until the next + * av_read_frame() or until avformat_close_input(). Otherwise the packet + * is valid indefinitely. In both cases the packet must be freed with + * av_packet_unref when it is no longer needed. For video, the packet contains + * exactly one frame. For audio, it contains an integer number of frames if each + * frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames + * have a variable size (e.g. MPEG audio), then it contains one frame. + * + * pkt->pts, pkt->dts and pkt->duration are always set to correct + * values in AVStream.time_base units (and guessed if the format cannot + * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format + * has B-frames, so it is better to rely on pkt->dts if you do not + * decompress the payload. + * + * @return 0 if OK, < 0 on error or end of file + */ +int av_read_frame(AVFormatContext *s, AVPacket *pkt); + +/** + * Seek to the keyframe at timestamp. + * 'timestamp' in 'stream_index'. + * + * @param s media file handle + * @param stream_index If stream_index is (-1), a default + * stream is selected, and timestamp is automatically converted + * from AV_TIME_BASE units to the stream specific time_base. + * @param timestamp Timestamp in AVStream.time_base units + * or, if no stream is specified, in AV_TIME_BASE units. + * @param flags flags which select direction and seeking mode + * @return >= 0 on success + */ +int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, + int flags); + +/** + * Seek to timestamp ts. + * Seeking will be done so that the point from which all active streams + * can be presented successfully will be closest to ts and within min/max_ts. + * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. + * + * If flags contain AVSEEK_FLAG_BYTE, then all timestamps are in bytes and + * are the file position (this may not be supported by all demuxers). + * If flags contain AVSEEK_FLAG_FRAME, then all timestamps are in frames + * in the stream with stream_index (this may not be supported by all demuxers). + * Otherwise all timestamps are in units of the stream selected by stream_index + * or if stream_index is -1, in AV_TIME_BASE units. + * If flags contain AVSEEK_FLAG_ANY, then non-keyframes are treated as + * keyframes (this may not be supported by all demuxers). + * If flags contain AVSEEK_FLAG_BACKWARD, it is ignored. + * + * @param s media file handle + * @param stream_index index of the stream which is used as time base reference + * @param min_ts smallest acceptable timestamp + * @param ts target timestamp + * @param max_ts largest acceptable timestamp + * @param flags flags + * @return >=0 on success, error code otherwise + * + * @note This is part of the new seek API which is still under construction. + * Thus do not use this yet. It may change at any time, do not expect + * ABI compatibility yet! + */ +int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); + +/** + * Discard all internally buffered data. This can be useful when dealing with + * discontinuities in the byte stream. Generally works only with formats that + * can resync. This includes headerless formats like MPEG-TS/TS but should also + * work with NUT, Ogg and in a limited way AVI for example. + * + * The set of streams, the detected duration, stream parameters and codecs do + * not change when calling this function. If you want a complete reset, it's + * better to open a new AVFormatContext. + * + * This does not flush the AVIOContext (s->pb). If necessary, call + * avio_flush(s->pb) before calling this function. + * + * @param s media file handle + * @return >=0 on success, error code otherwise + */ +int avformat_flush(AVFormatContext *s); + +/** + * Start playing a network-based stream (e.g. RTSP stream) at the + * current position. + */ +int av_read_play(AVFormatContext *s); + +/** + * Pause a network-based stream (e.g. RTSP stream). + * + * Use av_read_play() to resume it. + */ +int av_read_pause(AVFormatContext *s); + +/** + * Close an opened input AVFormatContext. Free it and all its contents + * and set *s to NULL. + */ +void avformat_close_input(AVFormatContext **s); +/** + * @} + */ + +#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward +#define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes +#define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non-keyframes +#define AVSEEK_FLAG_FRAME 8 ///< seeking based on frame number + +/** + * @addtogroup lavf_encoding + * @{ + */ + +#define AVSTREAM_INIT_IN_WRITE_HEADER 0 ///< stream parameters initialized in avformat_write_header +#define AVSTREAM_INIT_IN_INIT_OUTPUT 1 ///< stream parameters initialized in avformat_init_output + +/** + * Allocate the stream private data and write the stream header to + * an output media file. + * + * @param s Media file handle, must be allocated with avformat_alloc_context(). + * Its oformat field must be set to the desired output format; + * Its pb field must be set to an already opened AVIOContext. + * @param options An AVDictionary filled with AVFormatContext and muxer-private options. + * On return this parameter will be destroyed and replaced with a dict containing + * options that were not found. May be NULL. + * + * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec had not already been fully initialized in avformat_init, + * AVSTREAM_INIT_IN_INIT_OUTPUT on success if the codec had already been fully initialized in avformat_init, + * negative AVERROR on failure. + * + * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_init_output. + */ +av_warn_unused_result +int avformat_write_header(AVFormatContext *s, AVDictionary **options); + +/** + * Allocate the stream private data and initialize the codec, but do not write the header. + * May optionally be used before avformat_write_header to initialize stream parameters + * before actually writing the header. + * If using this function, do not pass the same options to avformat_write_header. + * + * @param s Media file handle, must be allocated with avformat_alloc_context(). + * Its oformat field must be set to the desired output format; + * Its pb field must be set to an already opened AVIOContext. + * @param options An AVDictionary filled with AVFormatContext and muxer-private options. + * On return this parameter will be destroyed and replaced with a dict containing + * options that were not found. May be NULL. + * + * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec requires avformat_write_header to fully initialize, + * AVSTREAM_INIT_IN_INIT_OUTPUT on success if the codec has been fully initialized, + * negative AVERROR on failure. + * + * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_write_header. + */ +av_warn_unused_result +int avformat_init_output(AVFormatContext *s, AVDictionary **options); + +/** + * Write a packet to an output media file. + * + * This function passes the packet directly to the muxer, without any buffering + * or reordering. The caller is responsible for correctly interleaving the + * packets if the format requires it. Callers that want libavformat to handle + * the interleaving should call av_interleaved_write_frame() instead of this + * function. + * + * @param s media file handle + * @param pkt The packet containing the data to be written. Note that unlike + * av_interleaved_write_frame(), this function does not take + * ownership of the packet passed to it (though some muxers may make + * an internal reference to the input packet). + * <br> + * This parameter can be NULL (at any time, not just at the end), in + * order to immediately flush data buffered within the muxer, for + * muxers that buffer up data internally before writing it to the + * output. + * <br> + * Packet's @ref AVPacket.stream_index "stream_index" field must be + * set to the index of the corresponding stream in @ref + * AVFormatContext.streams "s->streams". + * <br> + * The timestamps (@ref AVPacket.pts "pts", @ref AVPacket.dts "dts") + * must be set to correct values in the stream's timebase (unless the + * output format is flagged with the AVFMT_NOTIMESTAMPS flag, then + * they can be set to AV_NOPTS_VALUE). + * The dts for subsequent packets passed to this function must be strictly + * increasing when compared in their respective timebases (unless the + * output format is flagged with the AVFMT_TS_NONSTRICT, then they + * merely have to be nondecreasing). @ref AVPacket.duration + * "duration") should also be set if known. + * @return < 0 on error, = 0 if OK, 1 if flushed and there is no more data to flush + * + * @see av_interleaved_write_frame() + */ +int av_write_frame(AVFormatContext *s, AVPacket *pkt); + +/** + * Write a packet to an output media file ensuring correct interleaving. + * + * This function will buffer the packets internally as needed to make sure the + * packets in the output file are properly interleaved in the order of + * increasing dts. Callers doing their own interleaving should call + * av_write_frame() instead of this function. + * + * Using this function instead of av_write_frame() can give muxers advance + * knowledge of future packets, improving e.g. the behaviour of the mp4 + * muxer for VFR content in fragmenting mode. + * + * @param s media file handle + * @param pkt The packet containing the data to be written. + * <br> + * If the packet is reference-counted, this function will take + * ownership of this reference and unreference it later when it sees + * fit. + * The caller must not access the data through this reference after + * this function returns. If the packet is not reference-counted, + * libavformat will make a copy. + * <br> + * This parameter can be NULL (at any time, not just at the end), to + * flush the interleaving queues. + * <br> + * Packet's @ref AVPacket.stream_index "stream_index" field must be + * set to the index of the corresponding stream in @ref + * AVFormatContext.streams "s->streams". + * <br> + * The timestamps (@ref AVPacket.pts "pts", @ref AVPacket.dts "dts") + * must be set to correct values in the stream's timebase (unless the + * output format is flagged with the AVFMT_NOTIMESTAMPS flag, then + * they can be set to AV_NOPTS_VALUE). + * The dts for subsequent packets in one stream must be strictly + * increasing (unless the output format is flagged with the + * AVFMT_TS_NONSTRICT, then they merely have to be nondecreasing). + * @ref AVPacket.duration "duration") should also be set if known. + * + * @return 0 on success, a negative AVERROR on error. Libavformat will always + * take care of freeing the packet, even if this function fails. + * + * @see av_write_frame(), AVFormatContext.max_interleave_delta + */ +int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt); + +/** + * Write an uncoded frame to an output media file. + * + * The frame must be correctly interleaved according to the container + * specification; if not, then av_interleaved_write_frame() must be used. + * + * See av_interleaved_write_frame() for details. + */ +int av_write_uncoded_frame(AVFormatContext *s, int stream_index, + AVFrame *frame); + +/** + * Write an uncoded frame to an output media file. + * + * If the muxer supports it, this function makes it possible to write an AVFrame + * structure directly, without encoding it into a packet. + * It is mostly useful for devices and similar special muxers that use raw + * video or PCM data and will not serialize it into a byte stream. + * + * To test whether it is possible to use it with a given muxer and stream, + * use av_write_uncoded_frame_query(). + * + * The caller gives up ownership of the frame and must not access it + * afterwards. + * + * @return >=0 for success, a negative code on error + */ +int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index, + AVFrame *frame); + +/** + * Test whether a muxer supports uncoded frame. + * + * @return >=0 if an uncoded frame can be written to that muxer and stream, + * <0 if not + */ +int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); + +/** + * Write the stream trailer to an output media file and free the + * file private data. + * + * May only be called after a successful call to avformat_write_header. + * + * @param s media file handle + * @return 0 if OK, AVERROR_xxx on error + */ +int av_write_trailer(AVFormatContext *s); + +/** + * Return the output format in the list of registered output formats + * which best matches the provided parameters, or return NULL if + * there is no match. + * + * @param short_name if non-NULL checks if short_name matches with the + * names of the registered formats + * @param filename if non-NULL checks if filename terminates with the + * extensions of the registered formats + * @param mime_type if non-NULL checks if mime_type matches with the + * MIME type of the registered formats + */ +AVOutputFormat *av_guess_format(const char *short_name, + const char *filename, + const char *mime_type); + +/** + * Guess the codec ID based upon muxer and filename. + */ +enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name, + const char *filename, const char *mime_type, + enum AVMediaType type); + +/** + * Get timing information for the data currently output. + * The exact meaning of "currently output" depends on the format. + * It is mostly relevant for devices that have an internal buffer and/or + * work in real time. + * @param s media file handle + * @param stream stream in the media file + * @param[out] dts DTS of the last packet output for the stream, in stream + * time_base units + * @param[out] wall absolute time when that packet whas output, + * in microsecond + * @return 0 if OK, AVERROR(ENOSYS) if the format does not support it + * Note: some formats or devices may not allow to measure dts and wall + * atomically. + */ +int av_get_output_timestamp(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); + + +/** + * @} + */ + + +/** + * @defgroup lavf_misc Utility functions + * @ingroup libavf + * @{ + * + * Miscellaneous utility functions related to both muxing and demuxing + * (or neither). + */ + +/** + * Send a nice hexadecimal dump of a buffer to the specified file stream. + * + * @param f The file stream pointer where the dump should be sent to. + * @param buf buffer + * @param size buffer size + * + * @see av_hex_dump_log, av_pkt_dump2, av_pkt_dump_log2 + */ +void av_hex_dump(FILE *f, const uint8_t *buf, int size); + +/** + * Send a nice hexadecimal dump of a buffer to the log. + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message, lower values signifying + * higher importance. + * @param buf buffer + * @param size buffer size + * + * @see av_hex_dump, av_pkt_dump2, av_pkt_dump_log2 + */ +void av_hex_dump_log(void *avcl, int level, const uint8_t *buf, int size); + +/** + * Send a nice dump of a packet to the specified file stream. + * + * @param f The file stream pointer where the dump should be sent to. + * @param pkt packet to dump + * @param dump_payload True if the payload must be displayed, too. + * @param st AVStream that the packet belongs to + */ +void av_pkt_dump2(FILE *f, const AVPacket *pkt, int dump_payload, const AVStream *st); + + +/** + * Send a nice dump of a packet to the log. + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message, lower values signifying + * higher importance. + * @param pkt packet to dump + * @param dump_payload True if the payload must be displayed, too. + * @param st AVStream that the packet belongs to + */ +void av_pkt_dump_log2(void *avcl, int level, const AVPacket *pkt, int dump_payload, + const AVStream *st); + +/** + * Get the AVCodecID for the given codec tag tag. + * If no codec id is found returns AV_CODEC_ID_NONE. + * + * @param tags list of supported codec_id-codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + * @param tag codec tag to match to a codec ID + */ +enum AVCodecID av_codec_get_id(const struct AVCodecTag * const *tags, unsigned int tag); + +/** + * Get the codec tag for the given codec id id. + * If no codec tag is found returns 0. + * + * @param tags list of supported codec_id-codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + * @param id codec ID to match to a codec tag + */ +unsigned int av_codec_get_tag(const struct AVCodecTag * const *tags, enum AVCodecID id); + +/** + * Get the codec tag for the given codec id. + * + * @param tags list of supported codec_id - codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + * @param id codec id that should be searched for in the list + * @param tag A pointer to the found tag + * @return 0 if id was not found in tags, > 0 if it was found + */ +int av_codec_get_tag2(const struct AVCodecTag * const *tags, enum AVCodecID id, + unsigned int *tag); + +int av_find_default_stream_index(AVFormatContext *s); + +/** + * Get the index for a specific timestamp. + * + * @param st stream that the timestamp belongs to + * @param timestamp timestamp to retrieve the index for + * @param flags if AVSEEK_FLAG_BACKWARD then the returned index will correspond + * to the timestamp which is <= the requested one, if backward + * is 0, then it will be >= + * if AVSEEK_FLAG_ANY seek to any frame, only keyframes otherwise + * @return < 0 if no such timestamp could be found + */ +int av_index_search_timestamp(AVStream *st, int64_t timestamp, int flags); + +/** + * Add an index entry into a sorted list. Update the entry if the list + * already contains it. + * + * @param timestamp timestamp in the time base of the given stream + */ +int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp, + int size, int distance, int flags); + + +/** + * Split a URL string into components. + * + * The pointers to buffers for storing individual components may be null, + * in order to ignore that component. Buffers for components not found are + * set to empty strings. If the port is not found, it is set to a negative + * value. + * + * @param proto the buffer for the protocol + * @param proto_size the size of the proto buffer + * @param authorization the buffer for the authorization + * @param authorization_size the size of the authorization buffer + * @param hostname the buffer for the host name + * @param hostname_size the size of the hostname buffer + * @param port_ptr a pointer to store the port number in + * @param path the buffer for the path + * @param path_size the size of the path buffer + * @param url the URL to split + */ +void av_url_split(char *proto, int proto_size, + char *authorization, int authorization_size, + char *hostname, int hostname_size, + int *port_ptr, + char *path, int path_size, + const char *url); + + +/** + * Print detailed information about the input or output format, such as + * duration, bitrate, streams, container, programs, metadata, side data, + * codec and time base. + * + * @param ic the context to analyze + * @param index index of the stream to dump information about + * @param url the URL to print, such as source or destination file + * @param is_output Select whether the specified context is an input(0) or output(1) + */ +void av_dump_format(AVFormatContext *ic, + int index, + const char *url, + int is_output); + + +#define AV_FRAME_FILENAME_FLAGS_MULTIPLE 1 ///< Allow multiple %d + +/** + * Return in 'buf' the path with '%d' replaced by a number. + * + * Also handles the '%0nd' format where 'n' is the total number + * of digits and '%%'. + * + * @param buf destination buffer + * @param buf_size destination buffer size + * @param path numbered sequence string + * @param number frame number + * @param flags AV_FRAME_FILENAME_FLAGS_* + * @return 0 if OK, -1 on format error + */ +int av_get_frame_filename2(char *buf, int buf_size, + const char *path, int number, int flags); + +int av_get_frame_filename(char *buf, int buf_size, + const char *path, int number); + +/** + * Check whether filename actually is a numbered sequence generator. + * + * @param filename possible numbered sequence string + * @return 1 if a valid numbered sequence string, 0 otherwise + */ +int av_filename_number_test(const char *filename); + +/** + * Generate an SDP for an RTP session. + * + * Note, this overwrites the id values of AVStreams in the muxer contexts + * for getting unique dynamic payload types. + * + * @param ac array of AVFormatContexts describing the RTP streams. If the + * array is composed by only one context, such context can contain + * multiple AVStreams (one AVStream per RTP stream). Otherwise, + * all the contexts in the array (an AVCodecContext per RTP stream) + * must contain only one AVStream. + * @param n_files number of AVCodecContexts contained in ac + * @param buf buffer where the SDP will be stored (must be allocated by + * the caller) + * @param size the size of the buffer + * @return 0 if OK, AVERROR_xxx on error + */ +int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size); + +/** + * Return a positive value if the given filename has one of the given + * extensions, 0 otherwise. + * + * @param filename file name to check against the given extensions + * @param extensions a comma-separated list of filename extensions + */ +int av_match_ext(const char *filename, const char *extensions); + +/** + * Test if the given container can store a codec. + * + * @param ofmt container to check for compatibility + * @param codec_id codec to potentially store in container + * @param std_compliance standards compliance level, one of FF_COMPLIANCE_* + * + * @return 1 if codec with ID codec_id can be stored in ofmt, 0 if it cannot. + * A negative number if this information is not available. + */ +int avformat_query_codec(const AVOutputFormat *ofmt, enum AVCodecID codec_id, + int std_compliance); + +/** + * @defgroup riff_fourcc RIFF FourCCs + * @{ + * Get the tables mapping RIFF FourCCs to libavcodec AVCodecIDs. The tables are + * meant to be passed to av_codec_get_id()/av_codec_get_tag() as in the + * following code: + * @code + * uint32_t tag = MKTAG('H', '2', '6', '4'); + * const struct AVCodecTag *table[] = { avformat_get_riff_video_tags(), 0 }; + * enum AVCodecID id = av_codec_get_id(table, tag); + * @endcode + */ +/** + * @return the table mapping RIFF FourCCs for video to libavcodec AVCodecID. + */ +const struct AVCodecTag *avformat_get_riff_video_tags(void); +/** + * @return the table mapping RIFF FourCCs for audio to AVCodecID. + */ +const struct AVCodecTag *avformat_get_riff_audio_tags(void); +/** + * @return the table mapping MOV FourCCs for video to libavcodec AVCodecID. + */ +const struct AVCodecTag *avformat_get_mov_video_tags(void); +/** + * @return the table mapping MOV FourCCs for audio to AVCodecID. + */ +const struct AVCodecTag *avformat_get_mov_audio_tags(void); + +/** + * @} + */ + +/** + * Guess the sample aspect ratio of a frame, based on both the stream and the + * frame aspect ratio. + * + * Since the frame aspect ratio is set by the codec but the stream aspect ratio + * is set by the demuxer, these two may not be equal. This function tries to + * return the value that you should use if you would like to display the frame. + * + * Basic logic is to use the stream aspect ratio if it is set to something sane + * otherwise use the frame aspect ratio. This way a container setting, which is + * usually easy to modify can override the coded value in the frames. + * + * @param format the format context which the stream is part of + * @param stream the stream which the frame is part of + * @param frame the frame with the aspect ratio to be determined + * @return the guessed (valid) sample_aspect_ratio, 0/1 if no idea + */ +AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame); + +/** + * Guess the frame rate, based on both the container and codec information. + * + * @param ctx the format context which the stream is part of + * @param stream the stream which the frame is part of + * @param frame the frame for which the frame rate should be determined, may be NULL + * @return the guessed (valid) frame rate, 0/1 if no idea + */ +AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, AVFrame *frame); + +/** + * Check if the stream st contained in s is matched by the stream specifier + * spec. + * + * See the "stream specifiers" chapter in the documentation for the syntax + * of spec. + * + * @return >0 if st is matched by spec; + * 0 if st is not matched by spec; + * AVERROR code if spec is invalid + * + * @note A stream specifier can match several streams in the format. + */ +int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, + const char *spec); + +int avformat_queue_attached_pictures(AVFormatContext *s); + +#if FF_API_OLD_BSF +/** + * Apply a list of bitstream filters to a packet. + * + * @param codec AVCodecContext, usually from an AVStream + * @param pkt the packet to apply filters to. If, on success, the returned + * packet has size == 0 and side_data_elems == 0, it indicates that + * the packet should be dropped + * @param bsfc a NULL-terminated list of filters to apply + * @return >=0 on success; + * AVERROR code on failure + */ +attribute_deprecated +int av_apply_bitstream_filters(AVCodecContext *codec, AVPacket *pkt, + AVBitStreamFilterContext *bsfc); +#endif + +enum AVTimebaseSource { + AVFMT_TBCF_AUTO = -1, + AVFMT_TBCF_DECODER, + AVFMT_TBCF_DEMUXER, +#if FF_API_R_FRAME_RATE + AVFMT_TBCF_R_FRAMERATE, +#endif +}; + +/** + * Transfer internal timing information from one stream to another. + * + * This function is useful when doing stream copy. + * + * @param ofmt target output format for ost + * @param ost output stream which needs timings copy and adjustments + * @param ist reference input stream to copy timings from + * @param copy_tb define from where the stream codec timebase needs to be imported + */ +int avformat_transfer_internal_stream_timing_info(const AVOutputFormat *ofmt, + AVStream *ost, const AVStream *ist, + enum AVTimebaseSource copy_tb); + +/** + * Get the internal codec timebase from a stream. + * + * @param st input stream to extract the timebase from + */ +AVRational av_stream_get_codec_timebase(const AVStream *st); + +/** + * @} + */ + +#endif /* AVFORMAT_AVFORMAT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avio.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avio.h new file mode 100644 index 0000000..75912ce --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avio.h
@@ -0,0 +1,861 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVFORMAT_AVIO_H +#define AVFORMAT_AVIO_H + +/** + * @file + * @ingroup lavf_io + * Buffered I/O operations + */ + +#include <stdint.h> + +#include "libavutil/common.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" + +#include "libavformat/version.h" + +/** + * Seeking works like for a local file. + */ +#define AVIO_SEEKABLE_NORMAL (1 << 0) + +/** + * Seeking by timestamp with avio_seek_time() is possible. + */ +#define AVIO_SEEKABLE_TIME (1 << 1) + +/** + * Callback for checking whether to abort blocking functions. + * AVERROR_EXIT is returned in this case by the interrupted + * function. During blocking operations, callback is called with + * opaque as parameter. If the callback returns 1, the + * blocking operation will be aborted. + * + * No members can be added to this struct without a major bump, if + * new elements have been added after this struct in AVFormatContext + * or AVIOContext. + */ +typedef struct AVIOInterruptCB { + int (*callback)(void*); + void *opaque; +} AVIOInterruptCB; + +/** + * Directory entry types. + */ +enum AVIODirEntryType { + AVIO_ENTRY_UNKNOWN, + AVIO_ENTRY_BLOCK_DEVICE, + AVIO_ENTRY_CHARACTER_DEVICE, + AVIO_ENTRY_DIRECTORY, + AVIO_ENTRY_NAMED_PIPE, + AVIO_ENTRY_SYMBOLIC_LINK, + AVIO_ENTRY_SOCKET, + AVIO_ENTRY_FILE, + AVIO_ENTRY_SERVER, + AVIO_ENTRY_SHARE, + AVIO_ENTRY_WORKGROUP, +}; + +/** + * Describes single entry of the directory. + * + * Only name and type fields are guaranteed be set. + * Rest of fields are protocol or/and platform dependent and might be unknown. + */ +typedef struct AVIODirEntry { + char *name; /**< Filename */ + int type; /**< Type of the entry */ + int utf8; /**< Set to 1 when name is encoded with UTF-8, 0 otherwise. + Name can be encoded with UTF-8 even though 0 is set. */ + int64_t size; /**< File size in bytes, -1 if unknown. */ + int64_t modification_timestamp; /**< Time of last modification in microseconds since unix + epoch, -1 if unknown. */ + int64_t access_timestamp; /**< Time of last access in microseconds since unix epoch, + -1 if unknown. */ + int64_t status_change_timestamp; /**< Time of last status change in microseconds since unix + epoch, -1 if unknown. */ + int64_t user_id; /**< User ID of owner, -1 if unknown. */ + int64_t group_id; /**< Group ID of owner, -1 if unknown. */ + int64_t filemode; /**< Unix file mode, -1 if unknown. */ +} AVIODirEntry; + +typedef struct AVIODirContext { + struct URLContext *url_context; +} AVIODirContext; + +/** + * Different data types that can be returned via the AVIO + * write_data_type callback. + */ +enum AVIODataMarkerType { + /** + * Header data; this needs to be present for the stream to be decodeable. + */ + AVIO_DATA_MARKER_HEADER, + /** + * A point in the output bytestream where a decoder can start decoding + * (i.e. a keyframe). A demuxer/decoder given the data flagged with + * AVIO_DATA_MARKER_HEADER, followed by any AVIO_DATA_MARKER_SYNC_POINT, + * should give decodeable results. + */ + AVIO_DATA_MARKER_SYNC_POINT, + /** + * A point in the output bytestream where a demuxer can start parsing + * (for non self synchronizing bytestream formats). That is, any + * non-keyframe packet start point. + */ + AVIO_DATA_MARKER_BOUNDARY_POINT, + /** + * This is any, unlabelled data. It can either be a muxer not marking + * any positions at all, it can be an actual boundary/sync point + * that the muxer chooses not to mark, or a later part of a packet/fragment + * that is cut into multiple write callbacks due to limited IO buffer size. + */ + AVIO_DATA_MARKER_UNKNOWN, + /** + * Trailer data, which doesn't contain actual content, but only for + * finalizing the output file. + */ + AVIO_DATA_MARKER_TRAILER, + /** + * A point in the output bytestream where the underlying AVIOContext might + * flush the buffer depending on latency or buffering requirements. Typically + * means the end of a packet. + */ + AVIO_DATA_MARKER_FLUSH_POINT, +}; + +/** + * Bytestream IO Context. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVIOContext) must not be used outside libav*. + * + * @note None of the function pointers in AVIOContext should be called + * directly, they should only be set by the client application + * when implementing custom I/O. Normally these are set to the + * function pointers specified in avio_alloc_context() + */ +typedef struct AVIOContext { + /** + * A class for private options. + * + * If this AVIOContext is created by avio_open2(), av_class is set and + * passes the options down to protocols. + * + * If this AVIOContext is manually allocated, then av_class may be set by + * the caller. + * + * warning -- this field can be NULL, be sure to not pass this AVIOContext + * to any av_opt_* functions in that case. + */ + const AVClass *av_class; + + /* + * The following shows the relationship between buffer, buf_ptr, + * buf_ptr_max, buf_end, buf_size, and pos, when reading and when writing + * (since AVIOContext is used for both): + * + ********************************************************************************** + * READING + ********************************************************************************** + * + * | buffer_size | + * |---------------------------------------| + * | | + * + * buffer buf_ptr buf_end + * +---------------+-----------------------+ + * |/ / / / / / / /|/ / / / / / /| | + * read buffer: |/ / consumed / | to be read /| | + * |/ / / / / / / /|/ / / / / / /| | + * +---------------+-----------------------+ + * + * pos + * +-------------------------------------------+-----------------+ + * input file: | | | + * +-------------------------------------------+-----------------+ + * + * + ********************************************************************************** + * WRITING + ********************************************************************************** + * + * | buffer_size | + * |--------------------------------------| + * | | + * + * buf_ptr_max + * buffer (buf_ptr) buf_end + * +-----------------------+--------------+ + * |/ / / / / / / / / / / /| | + * write buffer: | / / to be flushed / / | | + * |/ / / / / / / / / / / /| | + * +-----------------------+--------------+ + * buf_ptr can be in this + * due to a backward seek + * + * pos + * +-------------+----------------------------------------------+ + * output file: | | | + * +-------------+----------------------------------------------+ + * + */ + unsigned char *buffer; /**< Start of the buffer. */ + int buffer_size; /**< Maximum buffer size */ + unsigned char *buf_ptr; /**< Current position in the buffer */ + unsigned char *buf_end; /**< End of the data, may be less than + buffer+buffer_size if the read function returned + less data than requested, e.g. for streams where + no more data has been received yet. */ + void *opaque; /**< A private pointer, passed to the read/write/seek/... + functions. */ + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size); + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size); + int64_t (*seek)(void *opaque, int64_t offset, int whence); + int64_t pos; /**< position in the file of the current buffer */ + int eof_reached; /**< true if eof reached */ + int write_flag; /**< true if open for writing */ + int max_packet_size; + unsigned long checksum; + unsigned char *checksum_ptr; + unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size); + int error; /**< contains the error code or 0 if no error happened */ + /** + * Pause or resume playback for network streaming protocols - e.g. MMS. + */ + int (*read_pause)(void *opaque, int pause); + /** + * Seek to a given timestamp in stream with the specified stream_index. + * Needed for some network streaming protocols which don't support seeking + * to byte position. + */ + int64_t (*read_seek)(void *opaque, int stream_index, + int64_t timestamp, int flags); + /** + * A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable. + */ + int seekable; + + /** + * max filesize, used to limit allocations + * This field is internal to libavformat and access from outside is not allowed. + */ + int64_t maxsize; + + /** + * avio_read and avio_write should if possible be satisfied directly + * instead of going through a buffer, and avio_seek will always + * call the underlying seek function directly. + */ + int direct; + + /** + * Bytes read statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int64_t bytes_read; + + /** + * seek statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int seek_count; + + /** + * writeout statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int writeout_count; + + /** + * Original buffer size + * used internally after probing and ensure seekback to reset the buffer size + * This field is internal to libavformat and access from outside is not allowed. + */ + int orig_buffer_size; + + /** + * Threshold to favor readahead over seek. + * This is current internal only, do not use from outside. + */ + int short_seek_threshold; + + /** + * ',' separated list of allowed protocols. + */ + const char *protocol_whitelist; + + /** + * ',' separated list of disallowed protocols. + */ + const char *protocol_blacklist; + + /** + * A callback that is used instead of write_packet. + */ + int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size, + enum AVIODataMarkerType type, int64_t time); + /** + * If set, don't call write_data_type separately for AVIO_DATA_MARKER_BOUNDARY_POINT, + * but ignore them and treat them as AVIO_DATA_MARKER_UNKNOWN (to avoid needlessly + * small chunks of data returned from the callback). + */ + int ignore_boundary_point; + + /** + * Internal, not meant to be used from outside of AVIOContext. + */ + enum AVIODataMarkerType current_type; + int64_t last_time; + + /** + * A callback that is used instead of short_seek_threshold. + * This is current internal only, do not use from outside. + */ + int (*short_seek_get)(void *opaque); + + int64_t written; + + /** + * Maximum reached position before a backward seek in the write buffer, + * used keeping track of already written data for a later flush. + */ + unsigned char *buf_ptr_max; + + /** + * Try to buffer at least this amount of data before flushing it + */ + int min_packet_size; +} AVIOContext; + +/** + * Return the name of the protocol that will handle the passed URL. + * + * NULL is returned if no protocol could be found for the given URL. + * + * @return Name of the protocol or NULL. + */ +const char *avio_find_protocol_name(const char *url); + +/** + * Return AVIO_FLAG_* access flags corresponding to the access permissions + * of the resource in url, or a negative value corresponding to an + * AVERROR code in case of failure. The returned access flags are + * masked by the value in flags. + * + * @note This function is intrinsically unsafe, in the sense that the + * checked resource may change its existence or permission status from + * one call to another. Thus you should not trust the returned value, + * unless you are sure that no other processes are accessing the + * checked resource. + */ +int avio_check(const char *url, int flags); + +/** + * Move or rename a resource. + * + * @note url_src and url_dst should share the same protocol and authority. + * + * @param url_src url to resource to be moved + * @param url_dst new url to resource if the operation succeeded + * @return >=0 on success or negative on error. + */ +int avpriv_io_move(const char *url_src, const char *url_dst); + +/** + * Delete a resource. + * + * @param url resource to be deleted. + * @return >=0 on success or negative on error. + */ +int avpriv_io_delete(const char *url); + +/** + * Open directory for reading. + * + * @param s directory read context. Pointer to a NULL pointer must be passed. + * @param url directory to be listed. + * @param options A dictionary filled with protocol-private options. On return + * this parameter will be destroyed and replaced with a dictionary + * containing options that were not found. May be NULL. + * @return >=0 on success or negative on error. + */ +int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options); + +/** + * Get next directory entry. + * + * Returned entry must be freed with avio_free_directory_entry(). In particular + * it may outlive AVIODirContext. + * + * @param s directory read context. + * @param[out] next next entry or NULL when no more entries. + * @return >=0 on success or negative on error. End of list is not considered an + * error. + */ +int avio_read_dir(AVIODirContext *s, AVIODirEntry **next); + +/** + * Close directory. + * + * @note Entries created using avio_read_dir() are not deleted and must be + * freeded with avio_free_directory_entry(). + * + * @param s directory read context. + * @return >=0 on success or negative on error. + */ +int avio_close_dir(AVIODirContext **s); + +/** + * Free entry allocated by avio_read_dir(). + * + * @param entry entry to be freed. + */ +void avio_free_directory_entry(AVIODirEntry **entry); + +/** + * Allocate and initialize an AVIOContext for buffered I/O. It must be later + * freed with avio_context_free(). + * + * @param buffer Memory block for input/output operations via AVIOContext. + * The buffer must be allocated with av_malloc() and friends. + * It may be freed and replaced with a new buffer by libavformat. + * AVIOContext.buffer holds the buffer currently in use, + * which must be later freed with av_free(). + * @param buffer_size The buffer size is very important for performance. + * For protocols with fixed blocksize it should be set to this blocksize. + * For others a typical size is a cache page, e.g. 4kb. + * @param write_flag Set to 1 if the buffer should be writable, 0 otherwise. + * @param opaque An opaque pointer to user-specific data. + * @param read_packet A function for refilling the buffer, may be NULL. + * For stream protocols, must never return 0 but rather + * a proper AVERROR code. + * @param write_packet A function for writing the buffer contents, may be NULL. + * The function may not change the input buffers content. + * @param seek A function for seeking to specified byte position, may be NULL. + * + * @return Allocated AVIOContext or NULL on failure. + */ +AVIOContext *avio_alloc_context( + unsigned char *buffer, + int buffer_size, + int write_flag, + void *opaque, + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int64_t (*seek)(void *opaque, int64_t offset, int whence)); + +/** + * Free the supplied IO context and everything associated with it. + * + * @param s Double pointer to the IO context. This function will write NULL + * into s. + */ +void avio_context_free(AVIOContext **s); + +void avio_w8(AVIOContext *s, int b); +void avio_write(AVIOContext *s, const unsigned char *buf, int size); +void avio_wl64(AVIOContext *s, uint64_t val); +void avio_wb64(AVIOContext *s, uint64_t val); +void avio_wl32(AVIOContext *s, unsigned int val); +void avio_wb32(AVIOContext *s, unsigned int val); +void avio_wl24(AVIOContext *s, unsigned int val); +void avio_wb24(AVIOContext *s, unsigned int val); +void avio_wl16(AVIOContext *s, unsigned int val); +void avio_wb16(AVIOContext *s, unsigned int val); + +/** + * Write a NULL-terminated string. + * @return number of bytes written. + */ +int avio_put_str(AVIOContext *s, const char *str); + +/** + * Convert an UTF-8 string to UTF-16LE and write it. + * @param s the AVIOContext + * @param str NULL-terminated UTF-8 string + * + * @return number of bytes written. + */ +int avio_put_str16le(AVIOContext *s, const char *str); + +/** + * Convert an UTF-8 string to UTF-16BE and write it. + * @param s the AVIOContext + * @param str NULL-terminated UTF-8 string + * + * @return number of bytes written. + */ +int avio_put_str16be(AVIOContext *s, const char *str); + +/** + * Mark the written bytestream as a specific type. + * + * Zero-length ranges are omitted from the output. + * + * @param time the stream time the current bytestream pos corresponds to + * (in AV_TIME_BASE units), or AV_NOPTS_VALUE if unknown or not + * applicable + * @param type the kind of data written starting at the current pos + */ +void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType type); + +/** + * ORing this as the "whence" parameter to a seek function causes it to + * return the filesize without seeking anywhere. Supporting this is optional. + * If it is not supported then the seek function will return <0. + */ +#define AVSEEK_SIZE 0x10000 + +/** + * Passing this flag as the "whence" parameter to a seek function causes it to + * seek by any means (like reopening and linear reading) or other normally unreasonable + * means that can be extremely slow. + * This may be ignored by the seek code. + */ +#define AVSEEK_FORCE 0x20000 + +/** + * fseek() equivalent for AVIOContext. + * @return new position or AVERROR. + */ +int64_t avio_seek(AVIOContext *s, int64_t offset, int whence); + +/** + * Skip given number of bytes forward + * @return new position or AVERROR. + */ +int64_t avio_skip(AVIOContext *s, int64_t offset); + +/** + * ftell() equivalent for AVIOContext. + * @return position or AVERROR. + */ +static av_always_inline int64_t avio_tell(AVIOContext *s) +{ + return avio_seek(s, 0, SEEK_CUR); +} + +/** + * Get the filesize. + * @return filesize or AVERROR + */ +int64_t avio_size(AVIOContext *s); + +/** + * feof() equivalent for AVIOContext. + * @return non zero if and only if end of file + */ +int avio_feof(AVIOContext *s); + +/** @warning Writes up to 4 KiB per call */ +int avio_printf(AVIOContext *s, const char *fmt, ...) av_printf_format(2, 3); + +/** + * Force flushing of buffered data. + * + * For write streams, force the buffered data to be immediately written to the output, + * without to wait to fill the internal buffer. + * + * For read streams, discard all currently buffered data, and advance the + * reported file position to that of the underlying stream. This does not + * read new data, and does not perform any seeks. + */ +void avio_flush(AVIOContext *s); + +/** + * Read size bytes from AVIOContext into buf. + * @return number of bytes read or AVERROR + */ +int avio_read(AVIOContext *s, unsigned char *buf, int size); + +/** + * Read size bytes from AVIOContext into buf. Unlike avio_read(), this is allowed + * to read fewer bytes than requested. The missing bytes can be read in the next + * call. This always tries to read at least 1 byte. + * Useful to reduce latency in certain cases. + * @return number of bytes read or AVERROR + */ +int avio_read_partial(AVIOContext *s, unsigned char *buf, int size); + +/** + * @name Functions for reading from AVIOContext + * @{ + * + * @note return 0 if EOF, so you cannot use it if EOF handling is + * necessary + */ +int avio_r8 (AVIOContext *s); +unsigned int avio_rl16(AVIOContext *s); +unsigned int avio_rl24(AVIOContext *s); +unsigned int avio_rl32(AVIOContext *s); +uint64_t avio_rl64(AVIOContext *s); +unsigned int avio_rb16(AVIOContext *s); +unsigned int avio_rb24(AVIOContext *s); +unsigned int avio_rb32(AVIOContext *s); +uint64_t avio_rb64(AVIOContext *s); +/** + * @} + */ + +/** + * Read a string from pb into buf. The reading will terminate when either + * a NULL character was encountered, maxlen bytes have been read, or nothing + * more can be read from pb. The result is guaranteed to be NULL-terminated, it + * will be truncated if buf is too small. + * Note that the string is not interpreted or validated in any way, it + * might get truncated in the middle of a sequence for multi-byte encodings. + * + * @return number of bytes read (is always <= maxlen). + * If reading ends on EOF or error, the return value will be one more than + * bytes actually read. + */ +int avio_get_str(AVIOContext *pb, int maxlen, char *buf, int buflen); + +/** + * Read a UTF-16 string from pb and convert it to UTF-8. + * The reading will terminate when either a null or invalid character was + * encountered or maxlen bytes have been read. + * @return number of bytes read (is always <= maxlen) + */ +int avio_get_str16le(AVIOContext *pb, int maxlen, char *buf, int buflen); +int avio_get_str16be(AVIOContext *pb, int maxlen, char *buf, int buflen); + + +/** + * @name URL open modes + * The flags argument to avio_open must be one of the following + * constants, optionally ORed with other flags. + * @{ + */ +#define AVIO_FLAG_READ 1 /**< read-only */ +#define AVIO_FLAG_WRITE 2 /**< write-only */ +#define AVIO_FLAG_READ_WRITE (AVIO_FLAG_READ|AVIO_FLAG_WRITE) /**< read-write pseudo flag */ +/** + * @} + */ + +/** + * Use non-blocking mode. + * If this flag is set, operations on the context will return + * AVERROR(EAGAIN) if they can not be performed immediately. + * If this flag is not set, operations on the context will never return + * AVERROR(EAGAIN). + * Note that this flag does not affect the opening/connecting of the + * context. Connecting a protocol will always block if necessary (e.g. on + * network protocols) but never hang (e.g. on busy devices). + * Warning: non-blocking protocols is work-in-progress; this flag may be + * silently ignored. + */ +#define AVIO_FLAG_NONBLOCK 8 + +/** + * Use direct mode. + * avio_read and avio_write should if possible be satisfied directly + * instead of going through a buffer, and avio_seek will always + * call the underlying seek function directly. + */ +#define AVIO_FLAG_DIRECT 0x8000 + +/** + * Create and initialize a AVIOContext for accessing the + * resource indicated by url. + * @note When the resource indicated by url has been opened in + * read+write mode, the AVIOContext can be used only for writing. + * + * @param s Used to return the pointer to the created AVIOContext. + * In case of failure the pointed to value is set to NULL. + * @param url resource to access + * @param flags flags which control how the resource indicated by url + * is to be opened + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure + */ +int avio_open(AVIOContext **s, const char *url, int flags); + +/** + * Create and initialize a AVIOContext for accessing the + * resource indicated by url. + * @note When the resource indicated by url has been opened in + * read+write mode, the AVIOContext can be used only for writing. + * + * @param s Used to return the pointer to the created AVIOContext. + * In case of failure the pointed to value is set to NULL. + * @param url resource to access + * @param flags flags which control how the resource indicated by url + * is to be opened + * @param int_cb an interrupt callback to be used at the protocols level + * @param options A dictionary filled with protocol-private options. On return + * this parameter will be destroyed and replaced with a dict containing options + * that were not found. May be NULL. + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure + */ +int avio_open2(AVIOContext **s, const char *url, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options); + +/** + * Close the resource accessed by the AVIOContext s and free it. + * This function can only be used if s was opened by avio_open(). + * + * The internal buffer is automatically flushed before closing the + * resource. + * + * @return 0 on success, an AVERROR < 0 on error. + * @see avio_closep + */ +int avio_close(AVIOContext *s); + +/** + * Close the resource accessed by the AVIOContext *s, free it + * and set the pointer pointing to it to NULL. + * This function can only be used if s was opened by avio_open(). + * + * The internal buffer is automatically flushed before closing the + * resource. + * + * @return 0 on success, an AVERROR < 0 on error. + * @see avio_close + */ +int avio_closep(AVIOContext **s); + + +/** + * Open a write only memory stream. + * + * @param s new IO context + * @return zero if no error. + */ +int avio_open_dyn_buf(AVIOContext **s); + +/** + * Return the written size and a pointer to the buffer. + * The AVIOContext stream is left intact. + * The buffer must NOT be freed. + * No padding is added to the buffer. + * + * @param s IO context + * @param pbuffer pointer to a byte buffer + * @return the length of the byte buffer + */ +int avio_get_dyn_buf(AVIOContext *s, uint8_t **pbuffer); + +/** + * Return the written size and a pointer to the buffer. The buffer + * must be freed with av_free(). + * Padding of AV_INPUT_BUFFER_PADDING_SIZE is added to the buffer. + * + * @param s IO context + * @param pbuffer pointer to a byte buffer + * @return the length of the byte buffer + */ +int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer); + +/** + * Iterate through names of available protocols. + * + * @param opaque A private pointer representing current protocol. + * It must be a pointer to NULL on first iteration and will + * be updated by successive calls to avio_enum_protocols. + * @param output If set to 1, iterate over output protocols, + * otherwise over input protocols. + * + * @return A static string containing the name of current protocol or NULL + */ +const char *avio_enum_protocols(void **opaque, int output); + +/** + * Pause and resume playing - only meaningful if using a network streaming + * protocol (e.g. MMS). + * + * @param h IO context from which to call the read_pause function pointer + * @param pause 1 for pause, 0 for resume + */ +int avio_pause(AVIOContext *h, int pause); + +/** + * Seek to a given timestamp relative to some component stream. + * Only meaningful if using a network streaming protocol (e.g. MMS.). + * + * @param h IO context from which to call the seek function pointers + * @param stream_index The stream index that the timestamp is relative to. + * If stream_index is (-1) the timestamp should be in AV_TIME_BASE + * units from the beginning of the presentation. + * If a stream_index >= 0 is used and the protocol does not support + * seeking based on component streams, the call will fail. + * @param timestamp timestamp in AVStream.time_base units + * or if there is no stream specified then in AV_TIME_BASE units. + * @param flags Optional combination of AVSEEK_FLAG_BACKWARD, AVSEEK_FLAG_BYTE + * and AVSEEK_FLAG_ANY. The protocol may silently ignore + * AVSEEK_FLAG_BACKWARD and AVSEEK_FLAG_ANY, but AVSEEK_FLAG_BYTE will + * fail if used and not supported. + * @return >= 0 on success + * @see AVInputFormat::read_seek + */ +int64_t avio_seek_time(AVIOContext *h, int stream_index, + int64_t timestamp, int flags); + +/* Avoid a warning. The header can not be included because it breaks c++. */ +struct AVBPrint; + +/** + * Read contents of h into print buffer, up to max_size bytes, or up to EOF. + * + * @return 0 for success (max_size bytes read or EOF reached), negative error + * code otherwise + */ +int avio_read_to_bprint(AVIOContext *h, struct AVBPrint *pb, size_t max_size); + +/** + * Accept and allocate a client context on a server context. + * @param s the server context + * @param c the client context, must be unallocated + * @return >= 0 on success or a negative value corresponding + * to an AVERROR on failure + */ +int avio_accept(AVIOContext *s, AVIOContext **c); + +/** + * Perform one step of the protocol handshake to accept a new client. + * This function must be called on a client returned by avio_accept() before + * using it as a read/write context. + * It is separate from avio_accept() because it may block. + * A step of the handshake is defined by places where the application may + * decide to change the proceedings. + * For example, on a protocol with a request header and a reply header, each + * one can constitute a step because the application may use the parameters + * from the request to change parameters in the reply; or each individual + * chunk of the request can constitute a step. + * If the handshake is already finished, avio_handshake() does nothing and + * returns 0 immediately. + * + * @param c the client context to perform the handshake on + * @return 0 on a complete and successful handshake + * > 0 if the handshake progressed, but is not complete + * < 0 for an AVERROR code + */ +int avio_handshake(AVIOContext *c); +#endif /* AVFORMAT_AVIO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/version.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/version.h new file mode 100644 index 0000000..ac14a55 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/version.h
@@ -0,0 +1,111 @@ +/* + * Version macros. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_VERSION_H +#define AVFORMAT_VERSION_H + +/** + * @file + * @ingroup libavf + * Libavformat version macros + */ + +#include "libavutil/version.h" + +// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) +// Also please add any ticket numbers that you believe might be affected here +#define LIBAVFORMAT_VERSION_MAJOR 58 +#define LIBAVFORMAT_VERSION_MINOR 20 +#define LIBAVFORMAT_VERSION_MICRO 100 + +#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ + LIBAVFORMAT_VERSION_MINOR, \ + LIBAVFORMAT_VERSION_MICRO) +#define LIBAVFORMAT_VERSION AV_VERSION(LIBAVFORMAT_VERSION_MAJOR, \ + LIBAVFORMAT_VERSION_MINOR, \ + LIBAVFORMAT_VERSION_MICRO) +#define LIBAVFORMAT_BUILD LIBAVFORMAT_VERSION_INT + +#define LIBAVFORMAT_IDENT "Lavf" AV_STRINGIFY(LIBAVFORMAT_VERSION) + +/** + * FF_API_* defines may be placed below to indicate public API that will be + * dropped at a future version bump. The defines themselves are not part of + * the public API and may change, break or disappear at any time. + * + * @note, when bumping the major version it is recommended to manually + * disable each FF_API_* in its own commit instead of disabling them all + * at once through the bump. This improves the git bisect-ability of the change. + * + */ +#ifndef FF_API_COMPUTE_PKT_FIELDS2 +#define FF_API_COMPUTE_PKT_FIELDS2 (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_OPEN_CALLBACKS +#define FF_API_OLD_OPEN_CALLBACKS (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LAVF_AVCTX +#define FF_API_LAVF_AVCTX (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_HTTP_USER_AGENT +#define FF_API_HTTP_USER_AGENT (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_HLS_WRAP +#define FF_API_HLS_WRAP (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_HLS_USE_LOCALTIME +#define FF_API_HLS_USE_LOCALTIME (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LAVF_KEEPSIDE_FLAG +#define FF_API_LAVF_KEEPSIDE_FLAG (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_ROTATE_API +#define FF_API_OLD_ROTATE_API (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_FORMAT_GET_SET +#define FF_API_FORMAT_GET_SET (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_AVIO_EOF_0 +#define FF_API_OLD_AVIO_EOF_0 (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LAVF_FFSERVER +#define FF_API_LAVF_FFSERVER (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_FORMAT_FILENAME +#define FF_API_FORMAT_FILENAME (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_RTSP_OPTIONS +#define FF_API_OLD_RTSP_OPTIONS (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_NEXT +#define FF_API_NEXT (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_DASH_MIN_SEG_DURATION +#define FF_API_DASH_MIN_SEG_DURATION (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LAVF_MP4A_LATM +#define FF_API_LAVF_MP4A_LATM (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif + + +#ifndef FF_API_R_FRAME_RATE +#define FF_API_R_FRAME_RATE 1 +#endif +#endif /* AVFORMAT_VERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/avresample.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/avresample.h new file mode 100644 index 0000000..5ac9adb --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/avresample.h
@@ -0,0 +1,595 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_AVRESAMPLE_H +#define AVRESAMPLE_AVRESAMPLE_H + +/** + * @file + * @ingroup lavr + * external API header + */ + +/** + * @defgroup lavr libavresample + * @{ + * + * Libavresample (lavr) is a library that handles audio resampling, sample + * format conversion and mixing. + * + * Interaction with lavr is done through AVAudioResampleContext, which is + * allocated with avresample_alloc_context(). It is opaque, so all parameters + * must be set with the @ref avoptions API. + * + * For example the following code will setup conversion from planar float sample + * format to interleaved signed 16-bit integer, downsampling from 48kHz to + * 44.1kHz and downmixing from 5.1 channels to stereo (using the default mixing + * matrix): + * @code + * AVAudioResampleContext *avr = avresample_alloc_context(); + * av_opt_set_int(avr, "in_channel_layout", AV_CH_LAYOUT_5POINT1, 0); + * av_opt_set_int(avr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); + * av_opt_set_int(avr, "in_sample_rate", 48000, 0); + * av_opt_set_int(avr, "out_sample_rate", 44100, 0); + * av_opt_set_int(avr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); + * av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); + * @endcode + * + * Once the context is initialized, it must be opened with avresample_open(). If + * you need to change the conversion parameters, you must close the context with + * avresample_close(), change the parameters as described above, then reopen it + * again. + * + * The conversion itself is done by repeatedly calling avresample_convert(). + * Note that the samples may get buffered in two places in lavr. The first one + * is the output FIFO, where the samples end up if the output buffer is not + * large enough. The data stored in there may be retrieved at any time with + * avresample_read(). The second place is the resampling delay buffer, + * applicable only when resampling is done. The samples in it require more input + * before they can be processed. Their current amount is returned by + * avresample_get_delay(). At the end of conversion the resampling buffer can be + * flushed by calling avresample_convert() with NULL input. + * + * The following code demonstrates the conversion loop assuming the parameters + * from above and caller-defined functions get_input() and handle_output(): + * @code + * uint8_t **input; + * int in_linesize, in_samples; + * + * while (get_input(&input, &in_linesize, &in_samples)) { + * uint8_t *output + * int out_linesize; + * int out_samples = avresample_get_out_samples(avr, in_samples); + * + * av_samples_alloc(&output, &out_linesize, 2, out_samples, + * AV_SAMPLE_FMT_S16, 0); + * out_samples = avresample_convert(avr, &output, out_linesize, out_samples, + * input, in_linesize, in_samples); + * handle_output(output, out_linesize, out_samples); + * av_freep(&output); + * } + * @endcode + * + * When the conversion is finished and the FIFOs are flushed if required, the + * conversion context and everything associated with it must be freed with + * avresample_free(). + */ + +#include "libavutil/avutil.h" +#include "libavutil/channel_layout.h" +#include "libavutil/dict.h" +#include "libavutil/frame.h" +#include "libavutil/log.h" +#include "libavutil/mathematics.h" + +#include "libavresample/version.h" + +#define AVRESAMPLE_MAX_CHANNELS 32 + +typedef struct AVAudioResampleContext AVAudioResampleContext; + +/** + * @deprecated use libswresample + * + * Mixing Coefficient Types */ +enum attribute_deprecated AVMixCoeffType { + AV_MIX_COEFF_TYPE_Q8, /** 16-bit 8.8 fixed-point */ + AV_MIX_COEFF_TYPE_Q15, /** 32-bit 17.15 fixed-point */ + AV_MIX_COEFF_TYPE_FLT, /** floating-point */ + AV_MIX_COEFF_TYPE_NB, /** Number of coeff types. Not part of ABI */ +}; + +/** + * @deprecated use libswresample + * + * Resampling Filter Types */ +enum attribute_deprecated AVResampleFilterType { + AV_RESAMPLE_FILTER_TYPE_CUBIC, /**< Cubic */ + AV_RESAMPLE_FILTER_TYPE_BLACKMAN_NUTTALL, /**< Blackman Nuttall Windowed Sinc */ + AV_RESAMPLE_FILTER_TYPE_KAISER, /**< Kaiser Windowed Sinc */ +}; + +/** + * @deprecated use libswresample + */ +enum attribute_deprecated AVResampleDitherMethod { + AV_RESAMPLE_DITHER_NONE, /**< Do not use dithering */ + AV_RESAMPLE_DITHER_RECTANGULAR, /**< Rectangular Dither */ + AV_RESAMPLE_DITHER_TRIANGULAR, /**< Triangular Dither*/ + AV_RESAMPLE_DITHER_TRIANGULAR_HP, /**< Triangular Dither with High Pass */ + AV_RESAMPLE_DITHER_TRIANGULAR_NS, /**< Triangular Dither with Noise Shaping */ + AV_RESAMPLE_DITHER_NB, /**< Number of dither types. Not part of ABI. */ +}; + +/** + * + * @deprecated use libswresample + * + * Return the LIBAVRESAMPLE_VERSION_INT constant. + */ +attribute_deprecated +unsigned avresample_version(void); + +/** + * + * @deprecated use libswresample + * + * Return the libavresample build-time configuration. + * @return configure string + */ +attribute_deprecated +const char *avresample_configuration(void); + +/** + * + * @deprecated use libswresample + * + * Return the libavresample license. + */ +attribute_deprecated +const char *avresample_license(void); + +/** + * + * @deprecated use libswresample + * + * Get the AVClass for AVAudioResampleContext. + * + * Can be used in combination with AV_OPT_SEARCH_FAKE_OBJ for examining options + * without allocating a context. + * + * @see av_opt_find(). + * + * @return AVClass for AVAudioResampleContext + */ +attribute_deprecated +const AVClass *avresample_get_class(void); + +/** + * + * @deprecated use libswresample + * + * Allocate AVAudioResampleContext and set options. + * + * @return allocated audio resample context, or NULL on failure + */ +attribute_deprecated +AVAudioResampleContext *avresample_alloc_context(void); + +/** + * + * @deprecated use libswresample + * + * Initialize AVAudioResampleContext. + * @note The context must be configured using the AVOption API. + * @note The fields "in_channel_layout", "out_channel_layout", + * "in_sample_rate", "out_sample_rate", "in_sample_fmt", + * "out_sample_fmt" must be set. + * + * @see av_opt_set_int() + * @see av_opt_set_dict() + * @see av_get_default_channel_layout() + * + * @param avr audio resample context + * @return 0 on success, negative AVERROR code on failure + */ +attribute_deprecated +int avresample_open(AVAudioResampleContext *avr); + +/** + * + * @deprecated use libswresample + * + * Check whether an AVAudioResampleContext is open or closed. + * + * @param avr AVAudioResampleContext to check + * @return 1 if avr is open, 0 if avr is closed. + */ +attribute_deprecated +int avresample_is_open(AVAudioResampleContext *avr); + +/** + * + * @deprecated use libswresample + * + * Close AVAudioResampleContext. + * + * This closes the context, but it does not change the parameters. The context + * can be reopened with avresample_open(). It does, however, clear the output + * FIFO and any remaining leftover samples in the resampling delay buffer. If + * there was a custom matrix being used, that is also cleared. + * + * @see avresample_convert() + * @see avresample_set_matrix() + * + * @param avr audio resample context + */ +attribute_deprecated +void avresample_close(AVAudioResampleContext *avr); + +/** + * + * @deprecated use libswresample + * + * Free AVAudioResampleContext and associated AVOption values. + * + * This also calls avresample_close() before freeing. + * + * @param avr audio resample context + */ +attribute_deprecated +void avresample_free(AVAudioResampleContext **avr); + +/** + * + * @deprecated use libswresample + * + * Generate a channel mixing matrix. + * + * This function is the one used internally by libavresample for building the + * default mixing matrix. It is made public just as a utility function for + * building custom matrices. + * + * @param in_layout input channel layout + * @param out_layout output channel layout + * @param center_mix_level mix level for the center channel + * @param surround_mix_level mix level for the surround channel(s) + * @param lfe_mix_level mix level for the low-frequency effects channel + * @param normalize if 1, coefficients will be normalized to prevent + * overflow. if 0, coefficients will not be + * normalized. + * @param[out] matrix mixing coefficients; matrix[i + stride * o] is + * the weight of input channel i in output channel o. + * @param stride distance between adjacent input channels in the + * matrix array + * @param matrix_encoding matrixed stereo downmix mode (e.g. dplii) + * @return 0 on success, negative AVERROR code on failure + */ +attribute_deprecated +int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, + double center_mix_level, double surround_mix_level, + double lfe_mix_level, int normalize, double *matrix, + int stride, enum AVMatrixEncoding matrix_encoding); + +/** + * + * @deprecated use libswresample + * + * Get the current channel mixing matrix. + * + * If no custom matrix has been previously set or the AVAudioResampleContext is + * not open, an error is returned. + * + * @param avr audio resample context + * @param matrix mixing coefficients; matrix[i + stride * o] is the weight of + * input channel i in output channel o. + * @param stride distance between adjacent input channels in the matrix array + * @return 0 on success, negative AVERROR code on failure + */ +attribute_deprecated +int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, + int stride); + +/** + * + * @deprecated use libswresample + * + * Set channel mixing matrix. + * + * Allows for setting a custom mixing matrix, overriding the default matrix + * generated internally during avresample_open(). This function can be called + * anytime on an allocated context, either before or after calling + * avresample_open(), as long as the channel layouts have been set. + * avresample_convert() always uses the current matrix. + * Calling avresample_close() on the context will clear the current matrix. + * + * @see avresample_close() + * + * @param avr audio resample context + * @param matrix mixing coefficients; matrix[i + stride * o] is the weight of + * input channel i in output channel o. + * @param stride distance between adjacent input channels in the matrix array + * @return 0 on success, negative AVERROR code on failure + */ +attribute_deprecated +int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, + int stride); + +/** + * + * @deprecated use libswresample + * + * Set a customized input channel mapping. + * + * This function can only be called when the allocated context is not open. + * Also, the input channel layout must have already been set. + * + * Calling avresample_close() on the context will clear the channel mapping. + * + * The map for each input channel specifies the channel index in the source to + * use for that particular channel, or -1 to mute the channel. Source channels + * can be duplicated by using the same index for multiple input channels. + * + * Examples: + * + * Reordering 5.1 AAC order (C,L,R,Ls,Rs,LFE) to FFmpeg order (L,R,C,LFE,Ls,Rs): + * { 1, 2, 0, 5, 3, 4 } + * + * Muting the 3rd channel in 4-channel input: + * { 0, 1, -1, 3 } + * + * Duplicating the left channel of stereo input: + * { 0, 0 } + * + * @param avr audio resample context + * @param channel_map customized input channel mapping + * @return 0 on success, negative AVERROR code on failure + */ +attribute_deprecated +int avresample_set_channel_mapping(AVAudioResampleContext *avr, + const int *channel_map); + +/** + * + * @deprecated use libswresample + * + * Set compensation for resampling. + * + * This can be called anytime after avresample_open(). If resampling is not + * automatically enabled because of a sample rate conversion, the + * "force_resampling" option must have been set to 1 when opening the context + * in order to use resampling compensation. + * + * @param avr audio resample context + * @param sample_delta compensation delta, in samples + * @param compensation_distance compensation distance, in samples + * @return 0 on success, negative AVERROR code on failure + */ +attribute_deprecated +int avresample_set_compensation(AVAudioResampleContext *avr, int sample_delta, + int compensation_distance); + +/** + * + * @deprecated use libswresample + * + * Provide the upper bound on the number of samples the configured + * conversion would output. + * + * @param avr audio resample context + * @param in_nb_samples number of input samples + * + * @return number of samples or AVERROR(EINVAL) if the value + * would exceed INT_MAX + */ +attribute_deprecated +int avresample_get_out_samples(AVAudioResampleContext *avr, int in_nb_samples); + +/** + * + * @deprecated use libswresample + * + * Convert input samples and write them to the output FIFO. + * + * The upper bound on the number of output samples can be obtained through + * avresample_get_out_samples(). + * + * The output data can be NULL or have fewer allocated samples than required. + * In this case, any remaining samples not written to the output will be added + * to an internal FIFO buffer, to be returned at the next call to this function + * or to avresample_read(). + * + * If converting sample rate, there may be data remaining in the internal + * resampling delay buffer. avresample_get_delay() tells the number of remaining + * samples. To get this data as output, call avresample_convert() with NULL + * input. + * + * At the end of the conversion process, there may be data remaining in the + * internal FIFO buffer. avresample_available() tells the number of remaining + * samples. To get this data as output, either call avresample_convert() with + * NULL input or call avresample_read(). + * + * @see avresample_get_out_samples() + * @see avresample_read() + * @see avresample_get_delay() + * + * @param avr audio resample context + * @param output output data pointers + * @param out_plane_size output plane size, in bytes. + * This can be 0 if unknown, but that will lead to + * optimized functions not being used directly on the + * output, which could slow down some conversions. + * @param out_samples maximum number of samples that the output buffer can hold + * @param input input data pointers + * @param in_plane_size input plane size, in bytes + * This can be 0 if unknown, but that will lead to + * optimized functions not being used directly on the + * input, which could slow down some conversions. + * @param in_samples number of input samples to convert + * @return number of samples written to the output buffer, + * not including converted samples added to the internal + * output FIFO + */ +attribute_deprecated +int avresample_convert(AVAudioResampleContext *avr, uint8_t **output, + int out_plane_size, int out_samples, + uint8_t * const *input, int in_plane_size, + int in_samples); + +/** + * + * @deprecated use libswresample + * + * Return the number of samples currently in the resampling delay buffer. + * + * When resampling, there may be a delay between the input and output. Any + * unconverted samples in each call are stored internally in a delay buffer. + * This function allows the user to determine the current number of samples in + * the delay buffer, which can be useful for synchronization. + * + * @see avresample_convert() + * + * @param avr audio resample context + * @return number of samples currently in the resampling delay buffer + */ +attribute_deprecated +int avresample_get_delay(AVAudioResampleContext *avr); + +/** + * + * @deprecated use libswresample + * + * Return the number of available samples in the output FIFO. + * + * During conversion, if the user does not specify an output buffer or + * specifies an output buffer that is smaller than what is needed, remaining + * samples that are not written to the output are stored to an internal FIFO + * buffer. The samples in the FIFO can be read with avresample_read() or + * avresample_convert(). + * + * @see avresample_read() + * @see avresample_convert() + * + * @param avr audio resample context + * @return number of samples available for reading + */ +attribute_deprecated +int avresample_available(AVAudioResampleContext *avr); + +/** + * + * @deprecated use libswresample + * + * Read samples from the output FIFO. + * + * During conversion, if the user does not specify an output buffer or + * specifies an output buffer that is smaller than what is needed, remaining + * samples that are not written to the output are stored to an internal FIFO + * buffer. This function can be used to read samples from that internal FIFO. + * + * @see avresample_available() + * @see avresample_convert() + * + * @param avr audio resample context + * @param output output data pointers. May be NULL, in which case + * nb_samples of data is discarded from output FIFO. + * @param nb_samples number of samples to read from the FIFO + * @return the number of samples written to output + */ +attribute_deprecated +int avresample_read(AVAudioResampleContext *avr, uint8_t **output, int nb_samples); + +/** + * + * @deprecated use libswresample + * + * Convert the samples in the input AVFrame and write them to the output AVFrame. + * + * Input and output AVFrames must have channel_layout, sample_rate and format set. + * + * The upper bound on the number of output samples is obtained through + * avresample_get_out_samples(). + * + * If the output AVFrame does not have the data pointers allocated the nb_samples + * field will be set using avresample_get_out_samples() and av_frame_get_buffer() + * is called to allocate the frame. + * + * The output AVFrame can be NULL or have fewer allocated samples than required. + * In this case, any remaining samples not written to the output will be added + * to an internal FIFO buffer, to be returned at the next call to this function + * or to avresample_convert() or to avresample_read(). + * + * If converting sample rate, there may be data remaining in the internal + * resampling delay buffer. avresample_get_delay() tells the number of + * remaining samples. To get this data as output, call this function or + * avresample_convert() with NULL input. + * + * At the end of the conversion process, there may be data remaining in the + * internal FIFO buffer. avresample_available() tells the number of remaining + * samples. To get this data as output, either call this function or + * avresample_convert() with NULL input or call avresample_read(). + * + * If the AVAudioResampleContext configuration does not match the output and + * input AVFrame settings the conversion does not take place and depending on + * which AVFrame is not matching AVERROR_OUTPUT_CHANGED, AVERROR_INPUT_CHANGED + * or AVERROR_OUTPUT_CHANGED|AVERROR_INPUT_CHANGED is returned. + * + * @see avresample_get_out_samples() + * @see avresample_available() + * @see avresample_convert() + * @see avresample_read() + * @see avresample_get_delay() + * + * @param avr audio resample context + * @param output output AVFrame + * @param input input AVFrame + * @return 0 on success, AVERROR on failure or nonmatching + * configuration. + */ +attribute_deprecated +int avresample_convert_frame(AVAudioResampleContext *avr, + AVFrame *output, AVFrame *input); + +/** + * + * @deprecated use libswresample + * + * Configure or reconfigure the AVAudioResampleContext using the information + * provided by the AVFrames. + * + * The original resampling context is reset even on failure. + * The function calls avresample_close() internally if the context is open. + * + * @see avresample_open(); + * @see avresample_close(); + * + * @param avr audio resample context + * @param out output AVFrame + * @param in input AVFrame + * @return 0 on success, AVERROR on failure. + */ +attribute_deprecated +int avresample_config(AVAudioResampleContext *avr, AVFrame *out, AVFrame *in); + +/** + * @} + */ + +#endif /* AVRESAMPLE_AVRESAMPLE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/version.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/version.h new file mode 100644 index 0000000..d5d3ea8 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/version.h
@@ -0,0 +1,50 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_VERSION_H +#define AVRESAMPLE_VERSION_H + +/** + * @file + * @ingroup lavr + * Libavresample version macros. + */ + +#include "libavutil/version.h" + +#define LIBAVRESAMPLE_VERSION_MAJOR 4 +#define LIBAVRESAMPLE_VERSION_MINOR 0 +#define LIBAVRESAMPLE_VERSION_MICRO 0 + +#define LIBAVRESAMPLE_VERSION_INT AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \ + LIBAVRESAMPLE_VERSION_MINOR, \ + LIBAVRESAMPLE_VERSION_MICRO) +#define LIBAVRESAMPLE_VERSION AV_VERSION(LIBAVRESAMPLE_VERSION_MAJOR, \ + LIBAVRESAMPLE_VERSION_MINOR, \ + LIBAVRESAMPLE_VERSION_MICRO) +#define LIBAVRESAMPLE_BUILD LIBAVRESAMPLE_VERSION_INT + +#define LIBAVRESAMPLE_IDENT "Lavr" AV_STRINGIFY(LIBAVRESAMPLE_VERSION) + +/** + * FF_API_* defines may be placed below to indicate public API that will be + * dropped at a future version bump. The defines themselves are not part of + * the public API and may change, break or disappear at any time. + */ + +#endif /* AVRESAMPLE_VERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/adler32.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/adler32.h new file mode 100644 index 0000000..a1f035b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/adler32.h
@@ -0,0 +1,60 @@ +/* + * copyright (c) 2006 Mans Rullgard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_adler32 + * Public header for Adler-32 hash function implementation. + */ + +#ifndef AVUTIL_ADLER32_H +#define AVUTIL_ADLER32_H + +#include <stdint.h> +#include "attributes.h" + +/** + * @defgroup lavu_adler32 Adler-32 + * @ingroup lavu_hash + * Adler-32 hash function implementation. + * + * @{ + */ + +/** + * Calculate the Adler32 checksum of a buffer. + * + * Passing the return value to a subsequent av_adler32_update() call + * allows the checksum of multiple buffers to be calculated as though + * they were concatenated. + * + * @param adler initial checksum value + * @param buf pointer to input buffer + * @param len size of input buffer + * @return updated checksum + */ +unsigned long av_adler32_update(unsigned long adler, const uint8_t *buf, + unsigned int len) av_pure; + +/** + * @} + */ + +#endif /* AVUTIL_ADLER32_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes.h new file mode 100644 index 0000000..09efbda --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes.h
@@ -0,0 +1,65 @@ +/* + * copyright (c) 2007 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AES_H +#define AVUTIL_AES_H + +#include <stdint.h> + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_aes AES + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_aes_size; + +struct AVAES; + +/** + * Allocate an AVAES context. + */ +struct AVAES *av_aes_alloc(void); + +/** + * Initialize an AVAES context. + * @param key_bits 128, 192 or 256 + * @param decrypt 0 for encryption, 1 for decryption + */ +int av_aes_init(struct AVAES *a, const uint8_t *key, int key_bits, int decrypt); + +/** + * Encrypt or decrypt a buffer using a previously initialized context. + * @param count number of 16 byte blocks + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param iv initialization vector for CBC mode, if NULL then ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_aes_crypt(struct AVAES *a, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_AES_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes_ctr.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes_ctr.h new file mode 100644 index 0000000..e4aae12 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes_ctr.h
@@ -0,0 +1,88 @@ +/* + * AES-CTR cipher + * Copyright (c) 2015 Eran Kornblau <erankor at gmail dot com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AES_CTR_H +#define AVUTIL_AES_CTR_H + +#include <stdint.h> + +#include "attributes.h" +#include "version.h" + +#define AES_CTR_KEY_SIZE (16) +#define AES_CTR_IV_SIZE (8) + +struct AVAESCTR; + +/** + * Allocate an AVAESCTR context. + */ +struct AVAESCTR *av_aes_ctr_alloc(void); + +/** + * Initialize an AVAESCTR context. + * @param key encryption key, must have a length of AES_CTR_KEY_SIZE + */ +int av_aes_ctr_init(struct AVAESCTR *a, const uint8_t *key); + +/** + * Release an AVAESCTR context. + */ +void av_aes_ctr_free(struct AVAESCTR *a); + +/** + * Process a buffer using a previously initialized context. + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param size the size of src and dst + */ +void av_aes_ctr_crypt(struct AVAESCTR *a, uint8_t *dst, const uint8_t *src, int size); + +/** + * Get the current iv + */ +const uint8_t* av_aes_ctr_get_iv(struct AVAESCTR *a); + +/** + * Generate a random iv + */ +void av_aes_ctr_set_random_iv(struct AVAESCTR *a); + +/** + * Forcefully change the 8-byte iv + */ +void av_aes_ctr_set_iv(struct AVAESCTR *a, const uint8_t* iv); + +/** + * Forcefully change the "full" 16-byte iv, including the counter + */ +void av_aes_ctr_set_full_iv(struct AVAESCTR *a, const uint8_t* iv); + +/** + * Increment the top 64 bit of the iv (performed after each frame) + */ +void av_aes_ctr_increment_iv(struct AVAESCTR *a); + +/** + * @} + */ + +#endif /* AVUTIL_AES_CTR_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/attributes.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/attributes.h new file mode 100644 index 0000000..ced108a --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/attributes.h
@@ -0,0 +1,167 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Macro definitions for various function/variable attributes + */ + +#ifndef AVUTIL_ATTRIBUTES_H +#define AVUTIL_ATTRIBUTES_H + +#ifdef __GNUC__ +# define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y)) +# define AV_GCC_VERSION_AT_MOST(x,y) (__GNUC__ < (x) || __GNUC__ == (x) && __GNUC_MINOR__ <= (y)) +#else +# define AV_GCC_VERSION_AT_LEAST(x,y) 0 +# define AV_GCC_VERSION_AT_MOST(x,y) 0 +#endif + +#ifndef av_always_inline +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define av_always_inline __attribute__((always_inline)) inline +#elif defined(_MSC_VER) +# define av_always_inline __forceinline +#else +# define av_always_inline inline +#endif +#endif + +#ifndef av_extern_inline +#if defined(__ICL) && __ICL >= 1210 || defined(__GNUC_STDC_INLINE__) +# define av_extern_inline extern inline +#else +# define av_extern_inline inline +#endif +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,4) +# define av_warn_unused_result __attribute__((warn_unused_result)) +#else +# define av_warn_unused_result +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define av_noinline __attribute__((noinline)) +#elif defined(_MSC_VER) +# define av_noinline __declspec(noinline) +#else +# define av_noinline +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,1) || defined(__clang__) +# define av_pure __attribute__((pure)) +#else +# define av_pure +#endif + +#if AV_GCC_VERSION_AT_LEAST(2,6) || defined(__clang__) +# define av_const __attribute__((const)) +#else +# define av_const +#endif + +#if AV_GCC_VERSION_AT_LEAST(4,3) || defined(__clang__) +# define av_cold __attribute__((cold)) +#else +# define av_cold +#endif + +#if AV_GCC_VERSION_AT_LEAST(4,1) && !defined(__llvm__) +# define av_flatten __attribute__((flatten)) +#else +# define av_flatten +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define attribute_deprecated __attribute__((deprecated)) +#elif defined(_MSC_VER) +# define attribute_deprecated __declspec(deprecated) +#else +# define attribute_deprecated +#endif + +/** + * Disable warnings about deprecated features + * This is useful for sections of code kept for backward compatibility and + * scheduled for removal. + */ +#ifndef AV_NOWARN_DEPRECATED +#if AV_GCC_VERSION_AT_LEAST(4,6) +# define AV_NOWARN_DEPRECATED(code) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ + code \ + _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +# define AV_NOWARN_DEPRECATED(code) \ + __pragma(warning(push)) \ + __pragma(warning(disable : 4996)) \ + code; \ + __pragma(warning(pop)) +#else +# define AV_NOWARN_DEPRECATED(code) code +#endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define av_unused __attribute__((unused)) +#else +# define av_unused +#endif + +/** + * Mark a variable as used and prevent the compiler from optimizing it + * away. This is useful for variables accessed only from inline + * assembler without the compiler being aware. + */ +#if AV_GCC_VERSION_AT_LEAST(3,1) || defined(__clang__) +# define av_used __attribute__((used)) +#else +# define av_used +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,3) || defined(__clang__) +# define av_alias __attribute__((may_alias)) +#else +# define av_alias +#endif + +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__INTEL_COMPILER) +# define av_uninit(x) x=x +#else +# define av_uninit(x) x +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define av_builtin_constant_p __builtin_constant_p +# define av_printf_format(fmtpos, attrpos) __attribute__((__format__(__printf__, fmtpos, attrpos))) +#else +# define av_builtin_constant_p(x) 0 +# define av_printf_format(fmtpos, attrpos) +#endif + +#if AV_GCC_VERSION_AT_LEAST(2,5) || defined(__clang__) +# define av_noreturn __attribute__((noreturn)) +#else +# define av_noreturn +#endif + +#endif /* AVUTIL_ATTRIBUTES_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/audio_fifo.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/audio_fifo.h new file mode 100644 index 0000000..d8a9194 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/audio_fifo.h
@@ -0,0 +1,187 @@ +/* + * Audio FIFO + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Audio FIFO Buffer + */ + +#ifndef AVUTIL_AUDIO_FIFO_H +#define AVUTIL_AUDIO_FIFO_H + +#include "avutil.h" +#include "fifo.h" +#include "samplefmt.h" + +/** + * @addtogroup lavu_audio + * @{ + * + * @defgroup lavu_audiofifo Audio FIFO Buffer + * @{ + */ + +/** + * Context for an Audio FIFO Buffer. + * + * - Operates at the sample level rather than the byte level. + * - Supports multiple channels with either planar or packed sample format. + * - Automatic reallocation when writing to a full buffer. + */ +typedef struct AVAudioFifo AVAudioFifo; + +/** + * Free an AVAudioFifo. + * + * @param af AVAudioFifo to free + */ +void av_audio_fifo_free(AVAudioFifo *af); + +/** + * Allocate an AVAudioFifo. + * + * @param sample_fmt sample format + * @param channels number of channels + * @param nb_samples initial allocation size, in samples + * @return newly allocated AVAudioFifo, or NULL on error + */ +AVAudioFifo *av_audio_fifo_alloc(enum AVSampleFormat sample_fmt, int channels, + int nb_samples); + +/** + * Reallocate an AVAudioFifo. + * + * @param af AVAudioFifo to reallocate + * @param nb_samples new allocation size, in samples + * @return 0 if OK, or negative AVERROR code on failure + */ +av_warn_unused_result +int av_audio_fifo_realloc(AVAudioFifo *af, int nb_samples); + +/** + * Write data to an AVAudioFifo. + * + * The AVAudioFifo will be reallocated automatically if the available space + * is less than nb_samples. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param af AVAudioFifo to write to + * @param data audio data plane pointers + * @param nb_samples number of samples to write + * @return number of samples actually written, or negative AVERROR + * code on failure. If successful, the number of samples + * actually written will always be nb_samples. + */ +int av_audio_fifo_write(AVAudioFifo *af, void **data, int nb_samples); + +/** + * Peek data from an AVAudioFifo. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param af AVAudioFifo to read from + * @param data audio data plane pointers + * @param nb_samples number of samples to peek + * @return number of samples actually peek, or negative AVERROR code + * on failure. The number of samples actually peek will not + * be greater than nb_samples, and will only be less than + * nb_samples if av_audio_fifo_size is less than nb_samples. + */ +int av_audio_fifo_peek(AVAudioFifo *af, void **data, int nb_samples); + +/** + * Peek data from an AVAudioFifo. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param af AVAudioFifo to read from + * @param data audio data plane pointers + * @param nb_samples number of samples to peek + * @param offset offset from current read position + * @return number of samples actually peek, or negative AVERROR code + * on failure. The number of samples actually peek will not + * be greater than nb_samples, and will only be less than + * nb_samples if av_audio_fifo_size is less than nb_samples. + */ +int av_audio_fifo_peek_at(AVAudioFifo *af, void **data, int nb_samples, int offset); + +/** + * Read data from an AVAudioFifo. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param af AVAudioFifo to read from + * @param data audio data plane pointers + * @param nb_samples number of samples to read + * @return number of samples actually read, or negative AVERROR code + * on failure. The number of samples actually read will not + * be greater than nb_samples, and will only be less than + * nb_samples if av_audio_fifo_size is less than nb_samples. + */ +int av_audio_fifo_read(AVAudioFifo *af, void **data, int nb_samples); + +/** + * Drain data from an AVAudioFifo. + * + * Removes the data without reading it. + * + * @param af AVAudioFifo to drain + * @param nb_samples number of samples to drain + * @return 0 if OK, or negative AVERROR code on failure + */ +int av_audio_fifo_drain(AVAudioFifo *af, int nb_samples); + +/** + * Reset the AVAudioFifo buffer. + * + * This empties all data in the buffer. + * + * @param af AVAudioFifo to reset + */ +void av_audio_fifo_reset(AVAudioFifo *af); + +/** + * Get the current number of samples in the AVAudioFifo available for reading. + * + * @param af the AVAudioFifo to query + * @return number of samples available for reading + */ +int av_audio_fifo_size(AVAudioFifo *af); + +/** + * Get the current number of samples in the AVAudioFifo available for writing. + * + * @param af the AVAudioFifo to query + * @return number of samples available for writing + */ +int av_audio_fifo_space(AVAudioFifo *af); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_AUDIO_FIFO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avassert.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avassert.h new file mode 100644 index 0000000..9abeade --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avassert.h
@@ -0,0 +1,75 @@ +/* + * copyright (c) 2010 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * simple assert() macros that are a bit more flexible than ISO C assert(). + * @author Michael Niedermayer <michaelni@gmx.at> + */ + +#ifndef AVUTIL_AVASSERT_H +#define AVUTIL_AVASSERT_H + +#include <stdlib.h> +#include "avutil.h" +#include "log.h" + +/** + * assert() equivalent, that is always enabled. + */ +#define av_assert0(cond) do { \ + if (!(cond)) { \ + av_log(NULL, AV_LOG_PANIC, "Assertion %s failed at %s:%d\n", \ + AV_STRINGIFY(cond), __FILE__, __LINE__); \ + abort(); \ + } \ +} while (0) + + +/** + * assert() equivalent, that does not lie in speed critical code. + * These asserts() thus can be enabled without fearing speed loss. + */ +#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 0 +#define av_assert1(cond) av_assert0(cond) +#else +#define av_assert1(cond) ((void)0) +#endif + + +/** + * assert() equivalent, that does lie in speed critical code. + */ +#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1 +#define av_assert2(cond) av_assert0(cond) +#define av_assert2_fpu() av_assert0_fpu() +#else +#define av_assert2(cond) ((void)0) +#define av_assert2_fpu() ((void)0) +#endif + +/** + * Assert that floating point operations can be executed. + * + * This will av_assert0() that the cpu is not in MMX state on X86 + */ +void av_assert0_fpu(void); + +#endif /* AVUTIL_AVASSERT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avconfig.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avconfig.h new file mode 100644 index 0000000..c289fbb --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avconfig.h
@@ -0,0 +1,6 @@ +/* Generated by ffmpeg configure */ +#ifndef AVUTIL_AVCONFIG_H +#define AVUTIL_AVCONFIG_H +#define AV_HAVE_BIGENDIAN 0 +#define AV_HAVE_FAST_UNALIGNED 1 +#endif /* AVUTIL_AVCONFIG_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avstring.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avstring.h new file mode 100644 index 0000000..04d2695 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avstring.h
@@ -0,0 +1,407 @@ +/* + * Copyright (c) 2007 Mans Rullgard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AVSTRING_H +#define AVUTIL_AVSTRING_H + +#include <stddef.h> +#include <stdint.h> +#include "attributes.h" + +/** + * @addtogroup lavu_string + * @{ + */ + +/** + * Return non-zero if pfx is a prefix of str. If it is, *ptr is set to + * the address of the first character in str after the prefix. + * + * @param str input string + * @param pfx prefix to test + * @param ptr updated if the prefix is matched inside str + * @return non-zero if the prefix matches, zero otherwise + */ +int av_strstart(const char *str, const char *pfx, const char **ptr); + +/** + * Return non-zero if pfx is a prefix of str independent of case. If + * it is, *ptr is set to the address of the first character in str + * after the prefix. + * + * @param str input string + * @param pfx prefix to test + * @param ptr updated if the prefix is matched inside str + * @return non-zero if the prefix matches, zero otherwise + */ +int av_stristart(const char *str, const char *pfx, const char **ptr); + +/** + * Locate the first case-independent occurrence in the string haystack + * of the string needle. A zero-length string needle is considered to + * match at the start of haystack. + * + * This function is a case-insensitive version of the standard strstr(). + * + * @param haystack string to search in + * @param needle string to search for + * @return pointer to the located match within haystack + * or a null pointer if no match + */ +char *av_stristr(const char *haystack, const char *needle); + +/** + * Locate the first occurrence of the string needle in the string haystack + * where not more than hay_length characters are searched. A zero-length + * string needle is considered to match at the start of haystack. + * + * This function is a length-limited version of the standard strstr(). + * + * @param haystack string to search in + * @param needle string to search for + * @param hay_length length of string to search in + * @return pointer to the located match within haystack + * or a null pointer if no match + */ +char *av_strnstr(const char *haystack, const char *needle, size_t hay_length); + +/** + * Copy the string src to dst, but no more than size - 1 bytes, and + * null-terminate dst. + * + * This function is the same as BSD strlcpy(). + * + * @param dst destination buffer + * @param src source string + * @param size size of destination buffer + * @return the length of src + * + * @warning since the return value is the length of src, src absolutely + * _must_ be a properly 0-terminated string, otherwise this will read beyond + * the end of the buffer and possibly crash. + */ +size_t av_strlcpy(char *dst, const char *src, size_t size); + +/** + * Append the string src to the string dst, but to a total length of + * no more than size - 1 bytes, and null-terminate dst. + * + * This function is similar to BSD strlcat(), but differs when + * size <= strlen(dst). + * + * @param dst destination buffer + * @param src source string + * @param size size of destination buffer + * @return the total length of src and dst + * + * @warning since the return value use the length of src and dst, these + * absolutely _must_ be a properly 0-terminated strings, otherwise this + * will read beyond the end of the buffer and possibly crash. + */ +size_t av_strlcat(char *dst, const char *src, size_t size); + +/** + * Append output to a string, according to a format. Never write out of + * the destination buffer, and always put a terminating 0 within + * the buffer. + * @param dst destination buffer (string to which the output is + * appended) + * @param size total size of the destination buffer + * @param fmt printf-compatible format string, specifying how the + * following parameters are used + * @return the length of the string that would have been generated + * if enough space had been available + */ +size_t av_strlcatf(char *dst, size_t size, const char *fmt, ...) av_printf_format(3, 4); + +/** + * Get the count of continuous non zero chars starting from the beginning. + * + * @param len maximum number of characters to check in the string, that + * is the maximum value which is returned by the function + */ +static inline size_t av_strnlen(const char *s, size_t len) +{ + size_t i; + for (i = 0; i < len && s[i]; i++) + ; + return i; +} + +/** + * Print arguments following specified format into a large enough auto + * allocated buffer. It is similar to GNU asprintf(). + * @param fmt printf-compatible format string, specifying how the + * following parameters are used. + * @return the allocated string + * @note You have to free the string yourself with av_free(). + */ +char *av_asprintf(const char *fmt, ...) av_printf_format(1, 2); + +/** + * Convert a number to an av_malloced string. + */ +char *av_d2str(double d); + +/** + * Unescape the given string until a non escaped terminating char, + * and return the token corresponding to the unescaped string. + * + * The normal \ and ' escaping is supported. Leading and trailing + * whitespaces are removed, unless they are escaped with '\' or are + * enclosed between ''. + * + * @param buf the buffer to parse, buf will be updated to point to the + * terminating char + * @param term a 0-terminated list of terminating chars + * @return the malloced unescaped string, which must be av_freed by + * the user, NULL in case of allocation failure + */ +char *av_get_token(const char **buf, const char *term); + +/** + * Split the string into several tokens which can be accessed by + * successive calls to av_strtok(). + * + * A token is defined as a sequence of characters not belonging to the + * set specified in delim. + * + * On the first call to av_strtok(), s should point to the string to + * parse, and the value of saveptr is ignored. In subsequent calls, s + * should be NULL, and saveptr should be unchanged since the previous + * call. + * + * This function is similar to strtok_r() defined in POSIX.1. + * + * @param s the string to parse, may be NULL + * @param delim 0-terminated list of token delimiters, must be non-NULL + * @param saveptr user-provided pointer which points to stored + * information necessary for av_strtok() to continue scanning the same + * string. saveptr is updated to point to the next character after the + * first delimiter found, or to NULL if the string was terminated + * @return the found token, or NULL when no token is found + */ +char *av_strtok(char *s, const char *delim, char **saveptr); + +/** + * Locale-independent conversion of ASCII isdigit. + */ +static inline av_const int av_isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +/** + * Locale-independent conversion of ASCII isgraph. + */ +static inline av_const int av_isgraph(int c) +{ + return c > 32 && c < 127; +} + +/** + * Locale-independent conversion of ASCII isspace. + */ +static inline av_const int av_isspace(int c) +{ + return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || + c == '\v'; +} + +/** + * Locale-independent conversion of ASCII characters to uppercase. + */ +static inline av_const int av_toupper(int c) +{ + if (c >= 'a' && c <= 'z') + c ^= 0x20; + return c; +} + +/** + * Locale-independent conversion of ASCII characters to lowercase. + */ +static inline av_const int av_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + c ^= 0x20; + return c; +} + +/** + * Locale-independent conversion of ASCII isxdigit. + */ +static inline av_const int av_isxdigit(int c) +{ + c = av_tolower(c); + return av_isdigit(c) || (c >= 'a' && c <= 'f'); +} + +/** + * Locale-independent case-insensitive compare. + * @note This means only ASCII-range characters are case-insensitive + */ +int av_strcasecmp(const char *a, const char *b); + +/** + * Locale-independent case-insensitive compare. + * @note This means only ASCII-range characters are case-insensitive + */ +int av_strncasecmp(const char *a, const char *b, size_t n); + +/** + * Locale-independent strings replace. + * @note This means only ASCII-range characters are replace + */ +char *av_strireplace(const char *str, const char *from, const char *to); + +/** + * Thread safe basename. + * @param path the path, on DOS both \ and / are considered separators. + * @return pointer to the basename substring. + */ +const char *av_basename(const char *path); + +/** + * Thread safe dirname. + * @param path the path, on DOS both \ and / are considered separators. + * @return the path with the separator replaced by the string terminator or ".". + * @note the function may change the input string. + */ +const char *av_dirname(char *path); + +/** + * Match instances of a name in a comma-separated list of names. + * List entries are checked from the start to the end of the names list, + * the first match ends further processing. If an entry prefixed with '-' + * matches, then 0 is returned. The "ALL" list entry is considered to + * match all names. + * + * @param name Name to look for. + * @param names List of names. + * @return 1 on match, 0 otherwise. + */ +int av_match_name(const char *name, const char *names); + +/** + * Append path component to the existing path. + * Path separator '/' is placed between when needed. + * Resulting string have to be freed with av_free(). + * @param path base path + * @param component component to be appended + * @return new path or NULL on error. + */ +char *av_append_path_component(const char *path, const char *component); + +enum AVEscapeMode { + AV_ESCAPE_MODE_AUTO, ///< Use auto-selected escaping mode. + AV_ESCAPE_MODE_BACKSLASH, ///< Use backslash escaping. + AV_ESCAPE_MODE_QUOTE, ///< Use single-quote escaping. +}; + +/** + * Consider spaces special and escape them even in the middle of the + * string. + * + * This is equivalent to adding the whitespace characters to the special + * characters lists, except it is guaranteed to use the exact same list + * of whitespace characters as the rest of libavutil. + */ +#define AV_ESCAPE_FLAG_WHITESPACE (1 << 0) + +/** + * Escape only specified special characters. + * Without this flag, escape also any characters that may be considered + * special by av_get_token(), such as the single quote. + */ +#define AV_ESCAPE_FLAG_STRICT (1 << 1) + +/** + * Escape string in src, and put the escaped string in an allocated + * string in *dst, which must be freed with av_free(). + * + * @param dst pointer where an allocated string is put + * @param src string to escape, must be non-NULL + * @param special_chars string containing the special characters which + * need to be escaped, can be NULL + * @param mode escape mode to employ, see AV_ESCAPE_MODE_* macros. + * Any unknown value for mode will be considered equivalent to + * AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without + * notice. + * @param flags flags which control how to escape, see AV_ESCAPE_FLAG_ macros + * @return the length of the allocated string, or a negative error code in case of error + * @see av_bprint_escape() + */ +av_warn_unused_result +int av_escape(char **dst, const char *src, const char *special_chars, + enum AVEscapeMode mode, int flags); + +#define AV_UTF8_FLAG_ACCEPT_INVALID_BIG_CODES 1 ///< accept codepoints over 0x10FFFF +#define AV_UTF8_FLAG_ACCEPT_NON_CHARACTERS 2 ///< accept non-characters - 0xFFFE and 0xFFFF +#define AV_UTF8_FLAG_ACCEPT_SURROGATES 4 ///< accept UTF-16 surrogates codes +#define AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES 8 ///< exclude control codes not accepted by XML + +#define AV_UTF8_FLAG_ACCEPT_ALL \ + AV_UTF8_FLAG_ACCEPT_INVALID_BIG_CODES|AV_UTF8_FLAG_ACCEPT_NON_CHARACTERS|AV_UTF8_FLAG_ACCEPT_SURROGATES + +/** + * Read and decode a single UTF-8 code point (character) from the + * buffer in *buf, and update *buf to point to the next byte to + * decode. + * + * In case of an invalid byte sequence, the pointer will be updated to + * the next byte after the invalid sequence and the function will + * return an error code. + * + * Depending on the specified flags, the function will also fail in + * case the decoded code point does not belong to a valid range. + * + * @note For speed-relevant code a carefully implemented use of + * GET_UTF8() may be preferred. + * + * @param codep pointer used to return the parsed code in case of success. + * The value in *codep is set even in case the range check fails. + * @param bufp pointer to the address the first byte of the sequence + * to decode, updated by the function to point to the + * byte next after the decoded sequence + * @param buf_end pointer to the end of the buffer, points to the next + * byte past the last in the buffer. This is used to + * avoid buffer overreads (in case of an unfinished + * UTF-8 sequence towards the end of the buffer). + * @param flags a collection of AV_UTF8_FLAG_* flags + * @return >= 0 in case a sequence was successfully read, a negative + * value in case of invalid sequence + */ +av_warn_unused_result +int av_utf8_decode(int32_t *codep, const uint8_t **bufp, const uint8_t *buf_end, + unsigned int flags); + +/** + * Check if a name is in a list. + * @returns 0 if not found, or the 1 based index where it has been found in the + * list. + */ +int av_match_list(const char *name, const char *list, char separator); + +/** + * @} + */ + +#endif /* AVUTIL_AVSTRING_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avutil.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avutil.h new file mode 100644 index 0000000..4d63315 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avutil.h
@@ -0,0 +1,365 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AVUTIL_H +#define AVUTIL_AVUTIL_H + +/** + * @file + * @ingroup lavu + * Convenience header that includes @ref lavu "libavutil"'s core. + */ + +/** + * @mainpage + * + * @section ffmpeg_intro Introduction + * + * This document describes the usage of the different libraries + * provided by FFmpeg. + * + * @li @ref libavc "libavcodec" encoding/decoding library + * @li @ref lavfi "libavfilter" graph-based frame editing library + * @li @ref libavf "libavformat" I/O and muxing/demuxing library + * @li @ref lavd "libavdevice" special devices muxing/demuxing library + * @li @ref lavu "libavutil" common utility library + * @li @ref lswr "libswresample" audio resampling, format conversion and mixing + * @li @ref lpp "libpostproc" post processing library + * @li @ref libsws "libswscale" color conversion and scaling library + * + * @section ffmpeg_versioning Versioning and compatibility + * + * Each of the FFmpeg libraries contains a version.h header, which defines a + * major, minor and micro version number with the + * <em>LIBRARYNAME_VERSION_{MAJOR,MINOR,MICRO}</em> macros. The major version + * number is incremented with backward incompatible changes - e.g. removing + * parts of the public API, reordering public struct members, etc. The minor + * version number is incremented for backward compatible API changes or major + * new features - e.g. adding a new public function or a new decoder. The micro + * version number is incremented for smaller changes that a calling program + * might still want to check for - e.g. changing behavior in a previously + * unspecified situation. + * + * FFmpeg guarantees backward API and ABI compatibility for each library as long + * as its major version number is unchanged. This means that no public symbols + * will be removed or renamed. Types and names of the public struct members and + * values of public macros and enums will remain the same (unless they were + * explicitly declared as not part of the public API). Documented behavior will + * not change. + * + * In other words, any correct program that works with a given FFmpeg snapshot + * should work just as well without any changes with any later snapshot with the + * same major versions. This applies to both rebuilding the program against new + * FFmpeg versions or to replacing the dynamic FFmpeg libraries that a program + * links against. + * + * However, new public symbols may be added and new members may be appended to + * public structs whose size is not part of public ABI (most public structs in + * FFmpeg). New macros and enum values may be added. Behavior in undocumented + * situations may change slightly (and be documented). All those are accompanied + * by an entry in doc/APIchanges and incrementing either the minor or micro + * version number. + */ + +/** + * @defgroup lavu libavutil + * Common code shared across all FFmpeg libraries. + * + * @note + * libavutil is designed to be modular. In most cases, in order to use the + * functions provided by one component of libavutil you must explicitly include + * the specific header containing that feature. If you are only using + * media-related components, you could simply include libavutil/avutil.h, which + * brings in most of the "core" components. + * + * @{ + * + * @defgroup lavu_crypto Crypto and Hashing + * + * @{ + * @} + * + * @defgroup lavu_math Mathematics + * @{ + * + * @} + * + * @defgroup lavu_string String Manipulation + * + * @{ + * + * @} + * + * @defgroup lavu_mem Memory Management + * + * @{ + * + * @} + * + * @defgroup lavu_data Data Structures + * @{ + * + * @} + * + * @defgroup lavu_video Video related + * + * @{ + * + * @} + * + * @defgroup lavu_audio Audio related + * + * @{ + * + * @} + * + * @defgroup lavu_error Error Codes + * + * @{ + * + * @} + * + * @defgroup lavu_log Logging Facility + * + * @{ + * + * @} + * + * @defgroup lavu_misc Other + * + * @{ + * + * @defgroup preproc_misc Preprocessor String Macros + * + * @{ + * + * @} + * + * @defgroup version_utils Library Version Macros + * + * @{ + * + * @} + */ + + +/** + * @addtogroup lavu_ver + * @{ + */ + +/** + * Return the LIBAVUTIL_VERSION_INT constant. + */ +unsigned avutil_version(void); + +/** + * Return an informative version string. This usually is the actual release + * version number or a git commit description. This string has no fixed format + * and can change any time. It should never be parsed by code. + */ +const char *av_version_info(void); + +/** + * Return the libavutil build-time configuration. + */ +const char *avutil_configuration(void); + +/** + * Return the libavutil license. + */ +const char *avutil_license(void); + +/** + * @} + */ + +/** + * @addtogroup lavu_media Media Type + * @brief Media Type + */ + +enum AVMediaType { + AVMEDIA_TYPE_UNKNOWN = -1, ///< Usually treated as AVMEDIA_TYPE_DATA + AVMEDIA_TYPE_VIDEO, + AVMEDIA_TYPE_AUDIO, + AVMEDIA_TYPE_DATA, ///< Opaque data information usually continuous + AVMEDIA_TYPE_SUBTITLE, + AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse + AVMEDIA_TYPE_NB +}; + +/** + * Return a string describing the media_type enum, NULL if media_type + * is unknown. + */ +const char *av_get_media_type_string(enum AVMediaType media_type); + +/** + * @defgroup lavu_const Constants + * @{ + * + * @defgroup lavu_enc Encoding specific + * + * @note those definition should move to avcodec + * @{ + */ + +#define FF_LAMBDA_SHIFT 7 +#define FF_LAMBDA_SCALE (1<<FF_LAMBDA_SHIFT) +#define FF_QP2LAMBDA 118 ///< factor to convert from H.263 QP to lambda +#define FF_LAMBDA_MAX (256*128-1) + +#define FF_QUALITY_SCALE FF_LAMBDA_SCALE //FIXME maybe remove + +/** + * @} + * @defgroup lavu_time Timestamp specific + * + * FFmpeg internal timebase and timestamp definitions + * + * @{ + */ + +/** + * @brief Undefined timestamp value + * + * Usually reported by demuxer that work on containers that do not provide + * either pts or dts. + */ + +#define AV_NOPTS_VALUE ((int64_t)UINT64_C(0x8000000000000000)) + +/** + * Internal time base represented as integer + */ + +#define AV_TIME_BASE 1000000 + +/** + * Internal time base represented as fractional value + */ + +#define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE} + +/** + * @} + * @} + * @defgroup lavu_picture Image related + * + * AVPicture types, pixel formats and basic image planes manipulation. + * + * @{ + */ + +enum AVPictureType { + AV_PICTURE_TYPE_NONE = 0, ///< Undefined + AV_PICTURE_TYPE_I, ///< Intra + AV_PICTURE_TYPE_P, ///< Predicted + AV_PICTURE_TYPE_B, ///< Bi-dir predicted + AV_PICTURE_TYPE_S, ///< S(GMC)-VOP MPEG-4 + AV_PICTURE_TYPE_SI, ///< Switching Intra + AV_PICTURE_TYPE_SP, ///< Switching Predicted + AV_PICTURE_TYPE_BI, ///< BI type +}; + +/** + * Return a single letter to describe the given picture type + * pict_type. + * + * @param[in] pict_type the picture type @return a single character + * representing the picture type, '?' if pict_type is unknown + */ +char av_get_picture_type_char(enum AVPictureType pict_type); + +/** + * @} + */ + +#include "common.h" +#include "error.h" +#include "rational.h" +#include "version.h" +#include "macros.h" +#include "mathematics.h" +#include "log.h" +#include "pixfmt.h" + +/** + * Return x default pointer in case p is NULL. + */ +static inline void *av_x_if_null(const void *p, const void *x) +{ + return (void *)(intptr_t)(p ? p : x); +} + +/** + * Compute the length of an integer list. + * + * @param elsize size in bytes of each list element (only 1, 2, 4 or 8) + * @param term list terminator (usually 0 or -1) + * @param list pointer to the list + * @return length of the list, in elements, not counting the terminator + */ +unsigned av_int_list_length_for_size(unsigned elsize, + const void *list, uint64_t term) av_pure; + +/** + * Compute the length of an integer list. + * + * @param term list terminator (usually 0 or -1) + * @param list pointer to the list + * @return length of the list, in elements, not counting the terminator + */ +#define av_int_list_length(list, term) \ + av_int_list_length_for_size(sizeof(*(list)), list, term) + +/** + * Open a file using a UTF-8 filename. + * The API of this function matches POSIX fopen(), errors are returned through + * errno. + */ +FILE *av_fopen_utf8(const char *path, const char *mode); + +/** + * Return the fractional representation of the internal time base. + */ +AVRational av_get_time_base_q(void); + +#define AV_FOURCC_MAX_STRING_SIZE 32 + +#define av_fourcc2str(fourcc) av_fourcc_make_string((char[AV_FOURCC_MAX_STRING_SIZE]){0}, fourcc) + +/** + * Fill the provided buffer with a string containing a FourCC (four-character + * code) representation. + * + * @param buf a buffer with size in bytes of at least AV_FOURCC_MAX_STRING_SIZE + * @param fourcc the fourcc to represent + * @return the buffer in input + */ +char *av_fourcc_make_string(char *buf, uint32_t fourcc); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_AVUTIL_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/base64.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/base64.h new file mode 100644 index 0000000..2954c12 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/base64.h
@@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_BASE64_H +#define AVUTIL_BASE64_H + +#include <stdint.h> + +/** + * @defgroup lavu_base64 Base64 + * @ingroup lavu_crypto + * @{ + */ + +/** + * Decode a base64-encoded string. + * + * @param out buffer for decoded data + * @param in null-terminated input string + * @param out_size size in bytes of the out buffer, must be at + * least 3/4 of the length of in, that is AV_BASE64_DECODE_SIZE(strlen(in)) + * @return number of bytes written, or a negative value in case of + * invalid input + */ +int av_base64_decode(uint8_t *out, const char *in, int out_size); + +/** + * Calculate the output size in bytes needed to decode a base64 string + * with length x to a data buffer. + */ +#define AV_BASE64_DECODE_SIZE(x) ((x) * 3LL / 4) + +/** + * Encode data to base64 and null-terminate. + * + * @param out buffer for encoded data + * @param out_size size in bytes of the out buffer (including the + * null terminator), must be at least AV_BASE64_SIZE(in_size) + * @param in input buffer containing the data to encode + * @param in_size size in bytes of the in buffer + * @return out or NULL in case of error + */ +char *av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size); + +/** + * Calculate the output size needed to base64-encode x bytes to a + * null-terminated string. + */ +#define AV_BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1) + + /** + * @} + */ + +#endif /* AVUTIL_BASE64_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/blowfish.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/blowfish.h new file mode 100644 index 0000000..9e289a4 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/blowfish.h
@@ -0,0 +1,82 @@ +/* + * Blowfish algorithm + * Copyright (c) 2012 Samuel Pitoiset + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_BLOWFISH_H +#define AVUTIL_BLOWFISH_H + +#include <stdint.h> + +/** + * @defgroup lavu_blowfish Blowfish + * @ingroup lavu_crypto + * @{ + */ + +#define AV_BF_ROUNDS 16 + +typedef struct AVBlowfish { + uint32_t p[AV_BF_ROUNDS + 2]; + uint32_t s[4][256]; +} AVBlowfish; + +/** + * Allocate an AVBlowfish context. + */ +AVBlowfish *av_blowfish_alloc(void); + +/** + * Initialize an AVBlowfish context. + * + * @param ctx an AVBlowfish context + * @param key a key + * @param key_len length of the key + */ +void av_blowfish_init(struct AVBlowfish *ctx, const uint8_t *key, int key_len); + +/** + * Encrypt or decrypt a buffer using a previously initialized context. + * + * @param ctx an AVBlowfish context + * @param xl left four bytes halves of input to be encrypted + * @param xr right four bytes halves of input to be encrypted + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_blowfish_crypt_ecb(struct AVBlowfish *ctx, uint32_t *xl, uint32_t *xr, + int decrypt); + +/** + * Encrypt or decrypt a buffer using a previously initialized context. + * + * @param ctx an AVBlowfish context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, if NULL ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_blowfish_crypt(struct AVBlowfish *ctx, uint8_t *dst, const uint8_t *src, + int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_BLOWFISH_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bprint.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bprint.h new file mode 100644 index 0000000..c09b1ac --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bprint.h
@@ -0,0 +1,219 @@ +/* + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_BPRINT_H +#define AVUTIL_BPRINT_H + +#include <stdarg.h> + +#include "attributes.h" +#include "avstring.h" + +/** + * Define a structure with extra padding to a fixed size + * This helps ensuring binary compatibility with future versions. + */ + +#define FF_PAD_STRUCTURE(name, size, ...) \ +struct ff_pad_helper_##name { __VA_ARGS__ }; \ +typedef struct name { \ + __VA_ARGS__ \ + char reserved_padding[size - sizeof(struct ff_pad_helper_##name)]; \ +} name; + +/** + * Buffer to print data progressively + * + * The string buffer grows as necessary and is always 0-terminated. + * The content of the string is never accessed, and thus is + * encoding-agnostic and can even hold binary data. + * + * Small buffers are kept in the structure itself, and thus require no + * memory allocation at all (unless the contents of the buffer is needed + * after the structure goes out of scope). This is almost as lightweight as + * declaring a local "char buf[512]". + * + * The length of the string can go beyond the allocated size: the buffer is + * then truncated, but the functions still keep account of the actual total + * length. + * + * In other words, buf->len can be greater than buf->size and records the + * total length of what would have been to the buffer if there had been + * enough memory. + * + * Append operations do not need to be tested for failure: if a memory + * allocation fails, data stop being appended to the buffer, but the length + * is still updated. This situation can be tested with + * av_bprint_is_complete(). + * + * The size_max field determines several possible behaviours: + * + * size_max = -1 (= UINT_MAX) or any large value will let the buffer be + * reallocated as necessary, with an amortized linear cost. + * + * size_max = 0 prevents writing anything to the buffer: only the total + * length is computed. The write operations can then possibly be repeated in + * a buffer with exactly the necessary size + * (using size_init = size_max = len + 1). + * + * size_max = 1 is automatically replaced by the exact size available in the + * structure itself, thus ensuring no dynamic memory allocation. The + * internal buffer is large enough to hold a reasonable paragraph of text, + * such as the current paragraph. + */ + +FF_PAD_STRUCTURE(AVBPrint, 1024, + char *str; /**< string so far */ + unsigned len; /**< length so far */ + unsigned size; /**< allocated memory */ + unsigned size_max; /**< maximum allocated memory */ + char reserved_internal_buffer[1]; +) + +/** + * Convenience macros for special values for av_bprint_init() size_max + * parameter. + */ +#define AV_BPRINT_SIZE_UNLIMITED ((unsigned)-1) +#define AV_BPRINT_SIZE_AUTOMATIC 1 +#define AV_BPRINT_SIZE_COUNT_ONLY 0 + +/** + * Init a print buffer. + * + * @param buf buffer to init + * @param size_init initial size (including the final 0) + * @param size_max maximum size; + * 0 means do not write anything, just count the length; + * 1 is replaced by the maximum value for automatic storage; + * any large value means that the internal buffer will be + * reallocated as needed up to that limit; -1 is converted to + * UINT_MAX, the largest limit possible. + * Check also AV_BPRINT_SIZE_* macros. + */ +void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max); + +/** + * Init a print buffer using a pre-existing buffer. + * + * The buffer will not be reallocated. + * + * @param buf buffer structure to init + * @param buffer byte buffer to use for the string data + * @param size size of buffer + */ +void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size); + +/** + * Append a formatted string to a print buffer. + */ +void av_bprintf(AVBPrint *buf, const char *fmt, ...) av_printf_format(2, 3); + +/** + * Append a formatted string to a print buffer. + */ +void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg); + +/** + * Append char c n times to a print buffer. + */ +void av_bprint_chars(AVBPrint *buf, char c, unsigned n); + +/** + * Append data to a print buffer. + * + * param buf bprint buffer to use + * param data pointer to data + * param size size of data + */ +void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size); + +struct tm; +/** + * Append a formatted date and time to a print buffer. + * + * param buf bprint buffer to use + * param fmt date and time format string, see strftime() + * param tm broken-down time structure to translate + * + * @note due to poor design of the standard strftime function, it may + * produce poor results if the format string expands to a very long text and + * the bprint buffer is near the limit stated by the size_max option. + */ +void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm); + +/** + * Allocate bytes in the buffer for external use. + * + * @param[in] buf buffer structure + * @param[in] size required size + * @param[out] mem pointer to the memory area + * @param[out] actual_size size of the memory area after allocation; + * can be larger or smaller than size + */ +void av_bprint_get_buffer(AVBPrint *buf, unsigned size, + unsigned char **mem, unsigned *actual_size); + +/** + * Reset the string to "" but keep internal allocated data. + */ +void av_bprint_clear(AVBPrint *buf); + +/** + * Test if the print buffer is complete (not truncated). + * + * It may have been truncated due to a memory allocation failure + * or the size_max limit (compare size and size_max if necessary). + */ +static inline int av_bprint_is_complete(const AVBPrint *buf) +{ + return buf->len < buf->size; +} + +/** + * Finalize a print buffer. + * + * The print buffer can no longer be used afterwards, + * but the len and size fields are still valid. + * + * @arg[out] ret_str if not NULL, used to return a permanent copy of the + * buffer contents, or NULL if memory allocation fails; + * if NULL, the buffer is discarded and freed + * @return 0 for success or error code (probably AVERROR(ENOMEM)) + */ +int av_bprint_finalize(AVBPrint *buf, char **ret_str); + +/** + * Escape the content in src and append it to dstbuf. + * + * @param dstbuf already inited destination bprint buffer + * @param src string containing the text to escape + * @param special_chars string containing the special characters which + * need to be escaped, can be NULL + * @param mode escape mode to employ, see AV_ESCAPE_MODE_* macros. + * Any unknown value for mode will be considered equivalent to + * AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without + * notice. + * @param flags flags which control how to escape, see AV_ESCAPE_FLAG_* macros + */ +void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, + enum AVEscapeMode mode, int flags); + +#endif /* AVUTIL_BPRINT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bswap.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bswap.h new file mode 100644 index 0000000..91cb795 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bswap.h
@@ -0,0 +1,109 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * byte swapping routines + */ + +#ifndef AVUTIL_BSWAP_H +#define AVUTIL_BSWAP_H + +#include <stdint.h> +#include "libavutil/avconfig.h" +#include "attributes.h" + +#ifdef HAVE_AV_CONFIG_H + +#include "config.h" + +#if ARCH_AARCH64 +# include "aarch64/bswap.h" +#elif ARCH_ARM +# include "arm/bswap.h" +#elif ARCH_AVR32 +# include "avr32/bswap.h" +#elif ARCH_SH4 +# include "sh4/bswap.h" +#elif ARCH_X86 +# include "x86/bswap.h" +#endif + +#endif /* HAVE_AV_CONFIG_H */ + +#define AV_BSWAP16C(x) (((x) << 8 & 0xff00) | ((x) >> 8 & 0x00ff)) +#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16)) +#define AV_BSWAP64C(x) (AV_BSWAP32C(x) << 32 | AV_BSWAP32C((x) >> 32)) + +#define AV_BSWAPC(s, x) AV_BSWAP##s##C(x) + +#ifndef av_bswap16 +static av_always_inline av_const uint16_t av_bswap16(uint16_t x) +{ + x= (x>>8) | (x<<8); + return x; +} +#endif + +#ifndef av_bswap32 +static av_always_inline av_const uint32_t av_bswap32(uint32_t x) +{ + return AV_BSWAP32C(x); +} +#endif + +#ifndef av_bswap64 +static inline uint64_t av_const av_bswap64(uint64_t x) +{ + return (uint64_t)av_bswap32(x) << 32 | av_bswap32(x >> 32); +} +#endif + +// be2ne ... big-endian to native-endian +// le2ne ... little-endian to native-endian + +#if AV_HAVE_BIGENDIAN +#define av_be2ne16(x) (x) +#define av_be2ne32(x) (x) +#define av_be2ne64(x) (x) +#define av_le2ne16(x) av_bswap16(x) +#define av_le2ne32(x) av_bswap32(x) +#define av_le2ne64(x) av_bswap64(x) +#define AV_BE2NEC(s, x) (x) +#define AV_LE2NEC(s, x) AV_BSWAPC(s, x) +#else +#define av_be2ne16(x) av_bswap16(x) +#define av_be2ne32(x) av_bswap32(x) +#define av_be2ne64(x) av_bswap64(x) +#define av_le2ne16(x) (x) +#define av_le2ne32(x) (x) +#define av_le2ne64(x) (x) +#define AV_BE2NEC(s, x) AV_BSWAPC(s, x) +#define AV_LE2NEC(s, x) (x) +#endif + +#define AV_BE2NE16C(x) AV_BE2NEC(16, x) +#define AV_BE2NE32C(x) AV_BE2NEC(32, x) +#define AV_BE2NE64C(x) AV_BE2NEC(64, x) +#define AV_LE2NE16C(x) AV_LE2NEC(16, x) +#define AV_LE2NE32C(x) AV_LE2NEC(32, x) +#define AV_LE2NE64C(x) AV_LE2NEC(64, x) + +#endif /* AVUTIL_BSWAP_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/buffer.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/buffer.h new file mode 100644 index 0000000..73b6bd0 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/buffer.h
@@ -0,0 +1,291 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_buffer + * refcounted data buffer API + */ + +#ifndef AVUTIL_BUFFER_H +#define AVUTIL_BUFFER_H + +#include <stdint.h> + +/** + * @defgroup lavu_buffer AVBuffer + * @ingroup lavu_data + * + * @{ + * AVBuffer is an API for reference-counted data buffers. + * + * There are two core objects in this API -- AVBuffer and AVBufferRef. AVBuffer + * represents the data buffer itself; it is opaque and not meant to be accessed + * by the caller directly, but only through AVBufferRef. However, the caller may + * e.g. compare two AVBuffer pointers to check whether two different references + * are describing the same data buffer. AVBufferRef represents a single + * reference to an AVBuffer and it is the object that may be manipulated by the + * caller directly. + * + * There are two functions provided for creating a new AVBuffer with a single + * reference -- av_buffer_alloc() to just allocate a new buffer, and + * av_buffer_create() to wrap an existing array in an AVBuffer. From an existing + * reference, additional references may be created with av_buffer_ref(). + * Use av_buffer_unref() to free a reference (this will automatically free the + * data once all the references are freed). + * + * The convention throughout this API and the rest of FFmpeg is such that the + * buffer is considered writable if there exists only one reference to it (and + * it has not been marked as read-only). The av_buffer_is_writable() function is + * provided to check whether this is true and av_buffer_make_writable() will + * automatically create a new writable buffer when necessary. + * Of course nothing prevents the calling code from violating this convention, + * however that is safe only when all the existing references are under its + * control. + * + * @note Referencing and unreferencing the buffers is thread-safe and thus + * may be done from multiple threads simultaneously without any need for + * additional locking. + * + * @note Two different references to the same buffer can point to different + * parts of the buffer (i.e. their AVBufferRef.data will not be equal). + */ + +/** + * A reference counted buffer type. It is opaque and is meant to be used through + * references (AVBufferRef). + */ +typedef struct AVBuffer AVBuffer; + +/** + * A reference to a data buffer. + * + * The size of this struct is not a part of the public ABI and it is not meant + * to be allocated directly. + */ +typedef struct AVBufferRef { + AVBuffer *buffer; + + /** + * The data buffer. It is considered writable if and only if + * this is the only reference to the buffer, in which case + * av_buffer_is_writable() returns 1. + */ + uint8_t *data; + /** + * Size of data in bytes. + */ + int size; +} AVBufferRef; + +/** + * Allocate an AVBuffer of the given size using av_malloc(). + * + * @return an AVBufferRef of given size or NULL when out of memory + */ +AVBufferRef *av_buffer_alloc(int size); + +/** + * Same as av_buffer_alloc(), except the returned buffer will be initialized + * to zero. + */ +AVBufferRef *av_buffer_allocz(int size); + +/** + * Always treat the buffer as read-only, even when it has only one + * reference. + */ +#define AV_BUFFER_FLAG_READONLY (1 << 0) + +/** + * Create an AVBuffer from an existing array. + * + * If this function is successful, data is owned by the AVBuffer. The caller may + * only access data through the returned AVBufferRef and references derived from + * it. + * If this function fails, data is left untouched. + * @param data data array + * @param size size of data in bytes + * @param free a callback for freeing this buffer's data + * @param opaque parameter to be got for processing or passed to free + * @param flags a combination of AV_BUFFER_FLAG_* + * + * @return an AVBufferRef referring to data on success, NULL on failure. + */ +AVBufferRef *av_buffer_create(uint8_t *data, int size, + void (*free)(void *opaque, uint8_t *data), + void *opaque, int flags); + +/** + * Default free callback, which calls av_free() on the buffer data. + * This function is meant to be passed to av_buffer_create(), not called + * directly. + */ +void av_buffer_default_free(void *opaque, uint8_t *data); + +/** + * Create a new reference to an AVBuffer. + * + * @return a new AVBufferRef referring to the same AVBuffer as buf or NULL on + * failure. + */ +AVBufferRef *av_buffer_ref(AVBufferRef *buf); + +/** + * Free a given reference and automatically free the buffer if there are no more + * references to it. + * + * @param buf the reference to be freed. The pointer is set to NULL on return. + */ +void av_buffer_unref(AVBufferRef **buf); + +/** + * @return 1 if the caller may write to the data referred to by buf (which is + * true if and only if buf is the only reference to the underlying AVBuffer). + * Return 0 otherwise. + * A positive answer is valid until av_buffer_ref() is called on buf. + */ +int av_buffer_is_writable(const AVBufferRef *buf); + +/** + * @return the opaque parameter set by av_buffer_create. + */ +void *av_buffer_get_opaque(const AVBufferRef *buf); + +int av_buffer_get_ref_count(const AVBufferRef *buf); + +/** + * Create a writable reference from a given buffer reference, avoiding data copy + * if possible. + * + * @param buf buffer reference to make writable. On success, buf is either left + * untouched, or it is unreferenced and a new writable AVBufferRef is + * written in its place. On failure, buf is left untouched. + * @return 0 on success, a negative AVERROR on failure. + */ +int av_buffer_make_writable(AVBufferRef **buf); + +/** + * Reallocate a given buffer. + * + * @param buf a buffer reference to reallocate. On success, buf will be + * unreferenced and a new reference with the required size will be + * written in its place. On failure buf will be left untouched. *buf + * may be NULL, then a new buffer is allocated. + * @param size required new buffer size. + * @return 0 on success, a negative AVERROR on failure. + * + * @note the buffer is actually reallocated with av_realloc() only if it was + * initially allocated through av_buffer_realloc(NULL) and there is only one + * reference to it (i.e. the one passed to this function). In all other cases + * a new buffer is allocated and the data is copied. + */ +int av_buffer_realloc(AVBufferRef **buf, int size); + +/** + * @} + */ + +/** + * @defgroup lavu_bufferpool AVBufferPool + * @ingroup lavu_data + * + * @{ + * AVBufferPool is an API for a lock-free thread-safe pool of AVBuffers. + * + * Frequently allocating and freeing large buffers may be slow. AVBufferPool is + * meant to solve this in cases when the caller needs a set of buffers of the + * same size (the most obvious use case being buffers for raw video or audio + * frames). + * + * At the beginning, the user must call av_buffer_pool_init() to create the + * buffer pool. Then whenever a buffer is needed, call av_buffer_pool_get() to + * get a reference to a new buffer, similar to av_buffer_alloc(). This new + * reference works in all aspects the same way as the one created by + * av_buffer_alloc(). However, when the last reference to this buffer is + * unreferenced, it is returned to the pool instead of being freed and will be + * reused for subsequent av_buffer_pool_get() calls. + * + * When the caller is done with the pool and no longer needs to allocate any new + * buffers, av_buffer_pool_uninit() must be called to mark the pool as freeable. + * Once all the buffers are released, it will automatically be freed. + * + * Allocating and releasing buffers with this API is thread-safe as long as + * either the default alloc callback is used, or the user-supplied one is + * thread-safe. + */ + +/** + * The buffer pool. This structure is opaque and not meant to be accessed + * directly. It is allocated with av_buffer_pool_init() and freed with + * av_buffer_pool_uninit(). + */ +typedef struct AVBufferPool AVBufferPool; + +/** + * Allocate and initialize a buffer pool. + * + * @param size size of each buffer in this pool + * @param alloc a function that will be used to allocate new buffers when the + * pool is empty. May be NULL, then the default allocator will be used + * (av_buffer_alloc()). + * @return newly created buffer pool on success, NULL on error. + */ +AVBufferPool *av_buffer_pool_init(int size, AVBufferRef* (*alloc)(int size)); + +/** + * Allocate and initialize a buffer pool with a more complex allocator. + * + * @param size size of each buffer in this pool + * @param opaque arbitrary user data used by the allocator + * @param alloc a function that will be used to allocate new buffers when the + * pool is empty. + * @param pool_free a function that will be called immediately before the pool + * is freed. I.e. after av_buffer_pool_uninit() is called + * by the caller and all the frames are returned to the pool + * and freed. It is intended to uninitialize the user opaque + * data. + * @return newly created buffer pool on success, NULL on error. + */ +AVBufferPool *av_buffer_pool_init2(int size, void *opaque, + AVBufferRef* (*alloc)(void *opaque, int size), + void (*pool_free)(void *opaque)); + +/** + * Mark the pool as being available for freeing. It will actually be freed only + * once all the allocated buffers associated with the pool are released. Thus it + * is safe to call this function while some of the allocated buffers are still + * in use. + * + * @param pool pointer to the pool to be freed. It will be set to NULL. + */ +void av_buffer_pool_uninit(AVBufferPool **pool); + +/** + * Allocate a new AVBuffer, reusing an old buffer from the pool when available. + * This function may be called simultaneously from multiple threads. + * + * @return a reference to the new buffer on success, NULL on error. + */ +AVBufferRef *av_buffer_pool_get(AVBufferPool *pool); + +/** + * @} + */ + +#endif /* AVUTIL_BUFFER_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/camellia.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/camellia.h new file mode 100644 index 0000000..e674c9b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/camellia.h
@@ -0,0 +1,70 @@ +/* + * An implementation of the CAMELLIA algorithm as mentioned in RFC3713 + * Copyright (c) 2014 Supraja Meedinti + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_CAMELLIA_H +#define AVUTIL_CAMELLIA_H + +#include <stdint.h> + + +/** + * @file + * @brief Public header for libavutil CAMELLIA algorithm + * @defgroup lavu_camellia CAMELLIA + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_camellia_size; + +struct AVCAMELLIA; + +/** + * Allocate an AVCAMELLIA context + * To free the struct: av_free(ptr) + */ +struct AVCAMELLIA *av_camellia_alloc(void); + +/** + * Initialize an AVCAMELLIA context. + * + * @param ctx an AVCAMELLIA context + * @param key a key of 16, 24, 32 bytes used for encryption/decryption + * @param key_bits number of keybits: possible are 128, 192, 256 + */ +int av_camellia_init(struct AVCAMELLIA *ctx, const uint8_t *key, int key_bits); + +/** + * Encrypt or decrypt a buffer using a previously initialized context + * + * @param ctx an AVCAMELLIA context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 16 byte blocks + * @paran iv initialization vector for CBC mode, NULL for ECB mode + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_camellia_crypt(struct AVCAMELLIA *ctx, uint8_t *dst, const uint8_t *src, int count, uint8_t* iv, int decrypt); + +/** + * @} + */ +#endif /* AVUTIL_CAMELLIA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cast5.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cast5.h new file mode 100644 index 0000000..ad5b347 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cast5.h
@@ -0,0 +1,80 @@ +/* + * An implementation of the CAST128 algorithm as mentioned in RFC2144 + * Copyright (c) 2014 Supraja Meedinti + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_CAST5_H +#define AVUTIL_CAST5_H + +#include <stdint.h> + + +/** + * @file + * @brief Public header for libavutil CAST5 algorithm + * @defgroup lavu_cast5 CAST5 + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_cast5_size; + +struct AVCAST5; + +/** + * Allocate an AVCAST5 context + * To free the struct: av_free(ptr) + */ +struct AVCAST5 *av_cast5_alloc(void); +/** + * Initialize an AVCAST5 context. + * + * @param ctx an AVCAST5 context + * @param key a key of 5,6,...16 bytes used for encryption/decryption + * @param key_bits number of keybits: possible are 40,48,...,128 + * @return 0 on success, less than 0 on failure + */ +int av_cast5_init(struct AVCAST5 *ctx, const uint8_t *key, int key_bits); + +/** + * Encrypt or decrypt a buffer using a previously initialized context, ECB mode only + * + * @param ctx an AVCAST5 context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_cast5_crypt(struct AVCAST5 *ctx, uint8_t *dst, const uint8_t *src, int count, int decrypt); + +/** + * Encrypt or decrypt a buffer using a previously initialized context + * + * @param ctx an AVCAST5 context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, NULL for ECB mode + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_cast5_crypt2(struct AVCAST5 *ctx, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt); +/** + * @} + */ +#endif /* AVUTIL_CAST5_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/channel_layout.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/channel_layout.h new file mode 100644 index 0000000..50bb8f0 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/channel_layout.h
@@ -0,0 +1,232 @@ +/* + * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * Copyright (c) 2008 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_CHANNEL_LAYOUT_H +#define AVUTIL_CHANNEL_LAYOUT_H + +#include <stdint.h> + +/** + * @file + * audio channel layout utility functions + */ + +/** + * @addtogroup lavu_audio + * @{ + */ + +/** + * @defgroup channel_masks Audio channel masks + * + * A channel layout is a 64-bits integer with a bit set for every channel. + * The number of bits set must be equal to the number of channels. + * The value 0 means that the channel layout is not known. + * @note this data structure is not powerful enough to handle channels + * combinations that have the same channel multiple times, such as + * dual-mono. + * + * @{ + */ +#define AV_CH_FRONT_LEFT 0x00000001 +#define AV_CH_FRONT_RIGHT 0x00000002 +#define AV_CH_FRONT_CENTER 0x00000004 +#define AV_CH_LOW_FREQUENCY 0x00000008 +#define AV_CH_BACK_LEFT 0x00000010 +#define AV_CH_BACK_RIGHT 0x00000020 +#define AV_CH_FRONT_LEFT_OF_CENTER 0x00000040 +#define AV_CH_FRONT_RIGHT_OF_CENTER 0x00000080 +#define AV_CH_BACK_CENTER 0x00000100 +#define AV_CH_SIDE_LEFT 0x00000200 +#define AV_CH_SIDE_RIGHT 0x00000400 +#define AV_CH_TOP_CENTER 0x00000800 +#define AV_CH_TOP_FRONT_LEFT 0x00001000 +#define AV_CH_TOP_FRONT_CENTER 0x00002000 +#define AV_CH_TOP_FRONT_RIGHT 0x00004000 +#define AV_CH_TOP_BACK_LEFT 0x00008000 +#define AV_CH_TOP_BACK_CENTER 0x00010000 +#define AV_CH_TOP_BACK_RIGHT 0x00020000 +#define AV_CH_STEREO_LEFT 0x20000000 ///< Stereo downmix. +#define AV_CH_STEREO_RIGHT 0x40000000 ///< See AV_CH_STEREO_LEFT. +#define AV_CH_WIDE_LEFT 0x0000000080000000ULL +#define AV_CH_WIDE_RIGHT 0x0000000100000000ULL +#define AV_CH_SURROUND_DIRECT_LEFT 0x0000000200000000ULL +#define AV_CH_SURROUND_DIRECT_RIGHT 0x0000000400000000ULL +#define AV_CH_LOW_FREQUENCY_2 0x0000000800000000ULL + +/** Channel mask value used for AVCodecContext.request_channel_layout + to indicate that the user requests the channel order of the decoder output + to be the native codec channel order. */ +#define AV_CH_LAYOUT_NATIVE 0x8000000000000000ULL + +/** + * @} + * @defgroup channel_mask_c Audio channel layouts + * @{ + * */ +#define AV_CH_LAYOUT_MONO (AV_CH_FRONT_CENTER) +#define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT) +#define AV_CH_LAYOUT_2POINT1 (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_2_1 (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER) +#define AV_CH_LAYOUT_3POINT1 (AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_4POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_4POINT1 (AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_2_2 (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT) +#define AV_CH_LAYOUT_QUAD (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_5POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT) +#define AV_CH_LAYOUT_5POINT1 (AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_5POINT0_BACK (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_5POINT1_BACK (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_6POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_6POINT0_FRONT (AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) +#define AV_CH_LAYOUT_HEXAGONAL (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_6POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_6POINT1_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_6POINT1_FRONT (AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_7POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_7POINT0_FRONT (AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) +#define AV_CH_LAYOUT_7POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_7POINT1_WIDE (AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) +#define AV_CH_LAYOUT_7POINT1_WIDE_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) +#define AV_CH_LAYOUT_OCTAGONAL (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_HEXADECAGONAL (AV_CH_LAYOUT_OCTAGONAL|AV_CH_WIDE_LEFT|AV_CH_WIDE_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT|AV_CH_TOP_BACK_CENTER|AV_CH_TOP_FRONT_CENTER|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT) +#define AV_CH_LAYOUT_STEREO_DOWNMIX (AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT) + +enum AVMatrixEncoding { + AV_MATRIX_ENCODING_NONE, + AV_MATRIX_ENCODING_DOLBY, + AV_MATRIX_ENCODING_DPLII, + AV_MATRIX_ENCODING_DPLIIX, + AV_MATRIX_ENCODING_DPLIIZ, + AV_MATRIX_ENCODING_DOLBYEX, + AV_MATRIX_ENCODING_DOLBYHEADPHONE, + AV_MATRIX_ENCODING_NB +}; + +/** + * Return a channel layout id that matches name, or 0 if no match is found. + * + * name can be one or several of the following notations, + * separated by '+' or '|': + * - the name of an usual channel layout (mono, stereo, 4.0, quad, 5.0, + * 5.0(side), 5.1, 5.1(side), 7.1, 7.1(wide), downmix); + * - the name of a single channel (FL, FR, FC, LFE, BL, BR, FLC, FRC, BC, + * SL, SR, TC, TFL, TFC, TFR, TBL, TBC, TBR, DL, DR); + * - a number of channels, in decimal, followed by 'c', yielding + * the default channel layout for that number of channels (@see + * av_get_default_channel_layout); + * - a channel layout mask, in hexadecimal starting with "0x" (see the + * AV_CH_* macros). + * + * Example: "stereo+FC" = "2c+FC" = "2c+1c" = "0x7" + */ +uint64_t av_get_channel_layout(const char *name); + +/** + * Return a channel layout and the number of channels based on the specified name. + * + * This function is similar to (@see av_get_channel_layout), but can also parse + * unknown channel layout specifications. + * + * @param[in] name channel layout specification string + * @param[out] channel_layout parsed channel layout (0 if unknown) + * @param[out] nb_channels number of channels + * + * @return 0 on success, AVERROR(EINVAL) if the parsing fails. + */ +int av_get_extended_channel_layout(const char *name, uint64_t* channel_layout, int* nb_channels); + +/** + * Return a description of a channel layout. + * If nb_channels is <= 0, it is guessed from the channel_layout. + * + * @param buf put here the string containing the channel layout + * @param buf_size size in bytes of the buffer + */ +void av_get_channel_layout_string(char *buf, int buf_size, int nb_channels, uint64_t channel_layout); + +struct AVBPrint; +/** + * Append a description of a channel layout to a bprint buffer. + */ +void av_bprint_channel_layout(struct AVBPrint *bp, int nb_channels, uint64_t channel_layout); + +/** + * Return the number of channels in the channel layout. + */ +int av_get_channel_layout_nb_channels(uint64_t channel_layout); + +/** + * Return default channel layout for a given number of channels. + */ +int64_t av_get_default_channel_layout(int nb_channels); + +/** + * Get the index of a channel in channel_layout. + * + * @param channel a channel layout describing exactly one channel which must be + * present in channel_layout. + * + * @return index of channel in channel_layout on success, a negative AVERROR + * on error. + */ +int av_get_channel_layout_channel_index(uint64_t channel_layout, + uint64_t channel); + +/** + * Get the channel with the given index in channel_layout. + */ +uint64_t av_channel_layout_extract_channel(uint64_t channel_layout, int index); + +/** + * Get the name of a given channel. + * + * @return channel name on success, NULL on error. + */ +const char *av_get_channel_name(uint64_t channel); + +/** + * Get the description of a given channel. + * + * @param channel a channel layout with a single channel + * @return channel description on success, NULL on error + */ +const char *av_get_channel_description(uint64_t channel); + +/** + * Get the value and name of a standard channel layout. + * + * @param[in] index index in an internal list, starting at 0 + * @param[out] layout channel layout mask + * @param[out] name name of the layout + * @return 0 if the layout exists, + * <0 if index is beyond the limits + */ +int av_get_standard_channel_layout(unsigned index, uint64_t *layout, + const char **name); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_CHANNEL_LAYOUT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/common.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/common.h new file mode 100644 index 0000000..8db0291 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/common.h
@@ -0,0 +1,560 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * common internal and external API header + */ + +#ifndef AVUTIL_COMMON_H +#define AVUTIL_COMMON_H + +#if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) && !defined(UINT64_C) +#error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS +#endif + +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "attributes.h" +#include "macros.h" +#include "version.h" +#include "libavutil/avconfig.h" + +#if AV_HAVE_BIGENDIAN +# define AV_NE(be, le) (be) +#else +# define AV_NE(be, le) (le) +#endif + +//rounded division & shift +#define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b)) +/* assume b>0 */ +#define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) +/* Fast a/(1<<b) rounded toward +inf. Assume a>=0 and b>=0 */ +#define AV_CEIL_RSHIFT(a,b) (!av_builtin_constant_p(b) ? -((-(a)) >> (b)) \ + : ((a) + (1<<(b)) - 1) >> (b)) +/* Backwards compat. */ +#define FF_CEIL_RSHIFT AV_CEIL_RSHIFT + +#define FFUDIV(a,b) (((a)>0 ?(a):(a)-(b)+1) / (b)) +#define FFUMOD(a,b) ((a)-(b)*FFUDIV(a,b)) + +/** + * Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they + * are not representable as absolute values of their type. This is the same + * as with *abs() + * @see FFNABS() + */ +#define FFABS(a) ((a) >= 0 ? (a) : (-(a))) +#define FFSIGN(a) ((a) > 0 ? 1 : -1) + +/** + * Negative Absolute value. + * this works for all integers of all types. + * As with many macros, this evaluates its argument twice, it thus must not have + * a sideeffect, that is FFNABS(x++) has undefined behavior. + */ +#define FFNABS(a) ((a) <= 0 ? (a) : (-(a))) + +/** + * Comparator. + * For two numerical expressions x and y, gives 1 if x > y, -1 if x < y, and 0 + * if x == y. This is useful for instance in a qsort comparator callback. + * Furthermore, compilers are able to optimize this to branchless code, and + * there is no risk of overflow with signed types. + * As with many macros, this evaluates its argument multiple times, it thus + * must not have a side-effect. + */ +#define FFDIFFSIGN(x,y) (((x)>(y)) - ((x)<(y))) + +#define FFMAX(a,b) ((a) > (b) ? (a) : (b)) +#define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c) +#define FFMIN(a,b) ((a) > (b) ? (b) : (a)) +#define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c) + +#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) +#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) + +/* misc math functions */ + +#ifdef HAVE_AV_CONFIG_H +# include "config.h" +# include "intmath.h" +#endif + +/* Pull in unguarded fallback defines at the end of this file. */ +#include "common.h" + +#ifndef av_log2 +av_const int av_log2(unsigned v); +#endif + +#ifndef av_log2_16bit +av_const int av_log2_16bit(unsigned v); +#endif + +/** + * Clip a signed integer value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static av_always_inline av_const int av_clip_c(int a, int amin, int amax) +{ +#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 + if (amin > amax) abort(); +#endif + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** + * Clip a signed 64bit integer value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax) +{ +#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 + if (amin > amax) abort(); +#endif + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** + * Clip a signed integer value into the 0-255 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const uint8_t av_clip_uint8_c(int a) +{ + if (a&(~0xFF)) return (~a)>>31; + else return a; +} + +/** + * Clip a signed integer value into the -128,127 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const int8_t av_clip_int8_c(int a) +{ + if ((a+0x80U) & ~0xFF) return (a>>31) ^ 0x7F; + else return a; +} + +/** + * Clip a signed integer value into the 0-65535 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const uint16_t av_clip_uint16_c(int a) +{ + if (a&(~0xFFFF)) return (~a)>>31; + else return a; +} + +/** + * Clip a signed integer value into the -32768,32767 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const int16_t av_clip_int16_c(int a) +{ + if ((a+0x8000U) & ~0xFFFF) return (a>>31) ^ 0x7FFF; + else return a; +} + +/** + * Clip a signed 64-bit integer value into the -2147483648,2147483647 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const int32_t av_clipl_int32_c(int64_t a) +{ + if ((a+0x80000000u) & ~UINT64_C(0xFFFFFFFF)) return (int32_t)((a>>63) ^ 0x7FFFFFFF); + else return (int32_t)a; +} + +/** + * Clip a signed integer into the -(2^p),(2^p-1) range. + * @param a value to clip + * @param p bit position to clip at + * @return clipped value + */ +static av_always_inline av_const int av_clip_intp2_c(int a, int p) +{ + if (((unsigned)a + (1 << p)) & ~((2 << p) - 1)) + return (a >> 31) ^ ((1 << p) - 1); + else + return a; +} + +/** + * Clip a signed integer to an unsigned power of two range. + * @param a value to clip + * @param p bit position to clip at + * @return clipped value + */ +static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p) +{ + if (a & ~((1<<p) - 1)) return (~a) >> 31 & ((1<<p) - 1); + else return a; +} + +/** + * Clear high bits from an unsigned integer starting with specific bit position + * @param a value to clip + * @param p bit position to clip at + * @return clipped value + */ +static av_always_inline av_const unsigned av_mod_uintp2_c(unsigned a, unsigned p) +{ + return a & ((1 << p) - 1); +} + +/** + * Add two signed 32-bit values with saturation. + * + * @param a one value + * @param b another value + * @return sum with signed saturation + */ +static av_always_inline int av_sat_add32_c(int a, int b) +{ + return av_clipl_int32((int64_t)a + b); +} + +/** + * Add a doubled value to another value with saturation at both stages. + * + * @param a first value + * @param b value doubled and added to a + * @return sum sat(a + sat(2*b)) with signed saturation + */ +static av_always_inline int av_sat_dadd32_c(int a, int b) +{ + return av_sat_add32(a, av_sat_add32(b, b)); +} + +/** + * Subtract two signed 32-bit values with saturation. + * + * @param a one value + * @param b another value + * @return difference with signed saturation + */ +static av_always_inline int av_sat_sub32_c(int a, int b) +{ + return av_clipl_int32((int64_t)a - b); +} + +/** + * Subtract a doubled value from another value with saturation at both stages. + * + * @param a first value + * @param b value doubled and subtracted from a + * @return difference sat(a - sat(2*b)) with signed saturation + */ +static av_always_inline int av_sat_dsub32_c(int a, int b) +{ + return av_sat_sub32(a, av_sat_add32(b, b)); +} + +/** + * Clip a float value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static av_always_inline av_const float av_clipf_c(float a, float amin, float amax) +{ +#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 + if (amin > amax) abort(); +#endif + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** + * Clip a double value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static av_always_inline av_const double av_clipd_c(double a, double amin, double amax) +{ +#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 + if (amin > amax) abort(); +#endif + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** Compute ceil(log2(x)). + * @param x value used to compute ceil(log2(x)) + * @return computed ceiling of log2(x) + */ +static av_always_inline av_const int av_ceil_log2_c(int x) +{ + return av_log2((x - 1) << 1); +} + +/** + * Count number of bits set to one in x + * @param x value to count bits of + * @return the number of bits set to one in x + */ +static av_always_inline av_const int av_popcount_c(uint32_t x) +{ + x -= (x >> 1) & 0x55555555; + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + x += x >> 8; + return (x + (x >> 16)) & 0x3F; +} + +/** + * Count number of bits set to one in x + * @param x value to count bits of + * @return the number of bits set to one in x + */ +static av_always_inline av_const int av_popcount64_c(uint64_t x) +{ + return av_popcount((uint32_t)x) + av_popcount((uint32_t)(x >> 32)); +} + +static av_always_inline av_const int av_parity_c(uint32_t v) +{ + return av_popcount(v) & 1; +} + +#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24)) +#define MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) + +/** + * Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form. + * + * @param val Output value, must be an lvalue of type uint32_t. + * @param GET_BYTE Expression reading one byte from the input. + * Evaluated up to 7 times (4 for the currently + * assigned Unicode range). With a memory buffer + * input, this could be *ptr++. + * @param ERROR Expression to be evaluated on invalid input, + * typically a goto statement. + * + * @warning ERROR should not contain a loop control statement which + * could interact with the internal while loop, and should force an + * exit from the macro code (e.g. through a goto or a return) in order + * to prevent undefined results. + */ +#define GET_UTF8(val, GET_BYTE, ERROR)\ + val= (GET_BYTE);\ + {\ + uint32_t top = (val & 128) >> 1;\ + if ((val & 0xc0) == 0x80 || val >= 0xFE)\ + ERROR\ + while (val & top) {\ + int tmp= (GET_BYTE) - 128;\ + if(tmp>>6)\ + ERROR\ + val= (val<<6) + tmp;\ + top <<= 5;\ + }\ + val &= (top << 1) - 1;\ + } + +/** + * Convert a UTF-16 character (2 or 4 bytes) to its 32-bit UCS-4 encoded form. + * + * @param val Output value, must be an lvalue of type uint32_t. + * @param GET_16BIT Expression returning two bytes of UTF-16 data converted + * to native byte order. Evaluated one or two times. + * @param ERROR Expression to be evaluated on invalid input, + * typically a goto statement. + */ +#define GET_UTF16(val, GET_16BIT, ERROR)\ + val = GET_16BIT;\ + {\ + unsigned int hi = val - 0xD800;\ + if (hi < 0x800) {\ + val = GET_16BIT - 0xDC00;\ + if (val > 0x3FFU || hi > 0x3FFU)\ + ERROR\ + val += (hi<<10) + 0x10000;\ + }\ + }\ + +/** + * @def PUT_UTF8(val, tmp, PUT_BYTE) + * Convert a 32-bit Unicode character to its UTF-8 encoded form (up to 4 bytes long). + * @param val is an input-only argument and should be of type uint32_t. It holds + * a UCS-4 encoded Unicode character that is to be converted to UTF-8. If + * val is given as a function it is executed only once. + * @param tmp is a temporary variable and should be of type uint8_t. It + * represents an intermediate value during conversion that is to be + * output by PUT_BYTE. + * @param PUT_BYTE writes the converted UTF-8 bytes to any proper destination. + * It could be a function or a statement, and uses tmp as the input byte. + * For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be + * executed up to 4 times for values in the valid UTF-8 range and up to + * 7 times in the general case, depending on the length of the converted + * Unicode character. + */ +#define PUT_UTF8(val, tmp, PUT_BYTE)\ + {\ + int bytes, shift;\ + uint32_t in = val;\ + if (in < 0x80) {\ + tmp = in;\ + PUT_BYTE\ + } else {\ + bytes = (av_log2(in) + 4) / 5;\ + shift = (bytes - 1) * 6;\ + tmp = (256 - (256 >> bytes)) | (in >> shift);\ + PUT_BYTE\ + while (shift >= 6) {\ + shift -= 6;\ + tmp = 0x80 | ((in >> shift) & 0x3f);\ + PUT_BYTE\ + }\ + }\ + } + +/** + * @def PUT_UTF16(val, tmp, PUT_16BIT) + * Convert a 32-bit Unicode character to its UTF-16 encoded form (2 or 4 bytes). + * @param val is an input-only argument and should be of type uint32_t. It holds + * a UCS-4 encoded Unicode character that is to be converted to UTF-16. If + * val is given as a function it is executed only once. + * @param tmp is a temporary variable and should be of type uint16_t. It + * represents an intermediate value during conversion that is to be + * output by PUT_16BIT. + * @param PUT_16BIT writes the converted UTF-16 data to any proper destination + * in desired endianness. It could be a function or a statement, and uses tmp + * as the input byte. For example, PUT_BYTE could be "*output++ = tmp;" + * PUT_BYTE will be executed 1 or 2 times depending on input character. + */ +#define PUT_UTF16(val, tmp, PUT_16BIT)\ + {\ + uint32_t in = val;\ + if (in < 0x10000) {\ + tmp = in;\ + PUT_16BIT\ + } else {\ + tmp = 0xD800 | ((in - 0x10000) >> 10);\ + PUT_16BIT\ + tmp = 0xDC00 | ((in - 0x10000) & 0x3FF);\ + PUT_16BIT\ + }\ + }\ + + + +#include "mem.h" + +#ifdef HAVE_AV_CONFIG_H +# include "internal.h" +#endif /* HAVE_AV_CONFIG_H */ + +#endif /* AVUTIL_COMMON_H */ + +/* + * The following definitions are outside the multiple inclusion guard + * to ensure they are immediately available in intmath.h. + */ + +#ifndef av_ceil_log2 +# define av_ceil_log2 av_ceil_log2_c +#endif +#ifndef av_clip +# define av_clip av_clip_c +#endif +#ifndef av_clip64 +# define av_clip64 av_clip64_c +#endif +#ifndef av_clip_uint8 +# define av_clip_uint8 av_clip_uint8_c +#endif +#ifndef av_clip_int8 +# define av_clip_int8 av_clip_int8_c +#endif +#ifndef av_clip_uint16 +# define av_clip_uint16 av_clip_uint16_c +#endif +#ifndef av_clip_int16 +# define av_clip_int16 av_clip_int16_c +#endif +#ifndef av_clipl_int32 +# define av_clipl_int32 av_clipl_int32_c +#endif +#ifndef av_clip_intp2 +# define av_clip_intp2 av_clip_intp2_c +#endif +#ifndef av_clip_uintp2 +# define av_clip_uintp2 av_clip_uintp2_c +#endif +#ifndef av_mod_uintp2 +# define av_mod_uintp2 av_mod_uintp2_c +#endif +#ifndef av_sat_add32 +# define av_sat_add32 av_sat_add32_c +#endif +#ifndef av_sat_dadd32 +# define av_sat_dadd32 av_sat_dadd32_c +#endif +#ifndef av_sat_sub32 +# define av_sat_sub32 av_sat_sub32_c +#endif +#ifndef av_sat_dsub32 +# define av_sat_dsub32 av_sat_dsub32_c +#endif +#ifndef av_clipf +# define av_clipf av_clipf_c +#endif +#ifndef av_clipd +# define av_clipd av_clipd_c +#endif +#ifndef av_popcount +# define av_popcount av_popcount_c +#endif +#ifndef av_popcount64 +# define av_popcount64 av_popcount64_c +#endif +#ifndef av_parity +# define av_parity av_parity_c +#endif
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cpu.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cpu.h new file mode 100644 index 0000000..8bb9eb6 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cpu.h
@@ -0,0 +1,130 @@ +/* + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_CPU_H +#define AVUTIL_CPU_H + +#include <stddef.h> + +#include "attributes.h" + +#define AV_CPU_FLAG_FORCE 0x80000000 /* force usage of selected flags (OR) */ + + /* lower 16 bits - CPU features */ +#define AV_CPU_FLAG_MMX 0x0001 ///< standard MMX +#define AV_CPU_FLAG_MMXEXT 0x0002 ///< SSE integer functions or AMD MMX ext +#define AV_CPU_FLAG_MMX2 0x0002 ///< SSE integer functions or AMD MMX ext +#define AV_CPU_FLAG_3DNOW 0x0004 ///< AMD 3DNOW +#define AV_CPU_FLAG_SSE 0x0008 ///< SSE functions +#define AV_CPU_FLAG_SSE2 0x0010 ///< PIV SSE2 functions +#define AV_CPU_FLAG_SSE2SLOW 0x40000000 ///< SSE2 supported, but usually not faster + ///< than regular MMX/SSE (e.g. Core1) +#define AV_CPU_FLAG_3DNOWEXT 0x0020 ///< AMD 3DNowExt +#define AV_CPU_FLAG_SSE3 0x0040 ///< Prescott SSE3 functions +#define AV_CPU_FLAG_SSE3SLOW 0x20000000 ///< SSE3 supported, but usually not faster + ///< than regular MMX/SSE (e.g. Core1) +#define AV_CPU_FLAG_SSSE3 0x0080 ///< Conroe SSSE3 functions +#define AV_CPU_FLAG_SSSE3SLOW 0x4000000 ///< SSSE3 supported, but usually not faster +#define AV_CPU_FLAG_ATOM 0x10000000 ///< Atom processor, some SSSE3 instructions are slower +#define AV_CPU_FLAG_SSE4 0x0100 ///< Penryn SSE4.1 functions +#define AV_CPU_FLAG_SSE42 0x0200 ///< Nehalem SSE4.2 functions +#define AV_CPU_FLAG_AESNI 0x80000 ///< Advanced Encryption Standard functions +#define AV_CPU_FLAG_AVX 0x4000 ///< AVX functions: requires OS support even if YMM registers aren't used +#define AV_CPU_FLAG_AVXSLOW 0x8000000 ///< AVX supported, but slow when using YMM registers (e.g. Bulldozer) +#define AV_CPU_FLAG_XOP 0x0400 ///< Bulldozer XOP functions +#define AV_CPU_FLAG_FMA4 0x0800 ///< Bulldozer FMA4 functions +#define AV_CPU_FLAG_CMOV 0x1000 ///< supports cmov instruction +#define AV_CPU_FLAG_AVX2 0x8000 ///< AVX2 functions: requires OS support even if YMM registers aren't used +#define AV_CPU_FLAG_FMA3 0x10000 ///< Haswell FMA3 functions +#define AV_CPU_FLAG_BMI1 0x20000 ///< Bit Manipulation Instruction Set 1 +#define AV_CPU_FLAG_BMI2 0x40000 ///< Bit Manipulation Instruction Set 2 +#define AV_CPU_FLAG_AVX512 0x100000 ///< AVX-512 functions: requires OS support even if YMM/ZMM registers aren't used + +#define AV_CPU_FLAG_ALTIVEC 0x0001 ///< standard +#define AV_CPU_FLAG_VSX 0x0002 ///< ISA 2.06 +#define AV_CPU_FLAG_POWER8 0x0004 ///< ISA 2.07 + +#define AV_CPU_FLAG_ARMV5TE (1 << 0) +#define AV_CPU_FLAG_ARMV6 (1 << 1) +#define AV_CPU_FLAG_ARMV6T2 (1 << 2) +#define AV_CPU_FLAG_VFP (1 << 3) +#define AV_CPU_FLAG_VFPV3 (1 << 4) +#define AV_CPU_FLAG_NEON (1 << 5) +#define AV_CPU_FLAG_ARMV8 (1 << 6) +#define AV_CPU_FLAG_VFP_VM (1 << 7) ///< VFPv2 vector mode, deprecated in ARMv7-A and unavailable in various CPUs implementations +#define AV_CPU_FLAG_SETEND (1 <<16) + +/** + * Return the flags which specify extensions supported by the CPU. + * The returned value is affected by av_force_cpu_flags() if that was used + * before. So av_get_cpu_flags() can easily be used in an application to + * detect the enabled cpu flags. + */ +int av_get_cpu_flags(void); + +/** + * Disables cpu detection and forces the specified flags. + * -1 is a special case that disables forcing of specific flags. + */ +void av_force_cpu_flags(int flags); + +/** + * Set a mask on flags returned by av_get_cpu_flags(). + * This function is mainly useful for testing. + * Please use av_force_cpu_flags() and av_get_cpu_flags() instead which are more flexible + */ +attribute_deprecated void av_set_cpu_flags_mask(int mask); + +/** + * Parse CPU flags from a string. + * + * The returned flags contain the specified flags as well as related unspecified flags. + * + * This function exists only for compatibility with libav. + * Please use av_parse_cpu_caps() when possible. + * @return a combination of AV_CPU_* flags, negative on error. + */ +attribute_deprecated +int av_parse_cpu_flags(const char *s); + +/** + * Parse CPU caps from a string and update the given AV_CPU_* flags based on that. + * + * @return negative on error. + */ +int av_parse_cpu_caps(unsigned *flags, const char *s); + +/** + * @return the number of logical CPU cores present. + */ +int av_cpu_count(void); + +/** + * Get the maximum data alignment that may be required by FFmpeg. + * + * Note that this is affected by the build configuration and the CPU flags mask, + * so e.g. if the CPU supports AVX, but libavutil has been built with + * --disable-avx or the AV_CPU_FLAG_AVX flag has been disabled through + * av_set_cpu_flags_mask(), then this function will behave as if AVX is not + * present. + */ +size_t av_cpu_max_align(void); + +#endif /* AVUTIL_CPU_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/crc.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/crc.h new file mode 100644 index 0000000..47e22b4 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/crc.h
@@ -0,0 +1,100 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_crc32 + * Public header for CRC hash function implementation. + */ + +#ifndef AVUTIL_CRC_H +#define AVUTIL_CRC_H + +#include <stdint.h> +#include <stddef.h> +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_crc32 CRC + * @ingroup lavu_hash + * CRC (Cyclic Redundancy Check) hash function implementation. + * + * This module supports numerous CRC polynomials, in addition to the most + * widely used CRC-32-IEEE. See @ref AVCRCId for a list of available + * polynomials. + * + * @{ + */ + +typedef uint32_t AVCRC; + +typedef enum { + AV_CRC_8_ATM, + AV_CRC_16_ANSI, + AV_CRC_16_CCITT, + AV_CRC_32_IEEE, + AV_CRC_32_IEEE_LE, /*< reversed bitorder version of AV_CRC_32_IEEE */ + AV_CRC_16_ANSI_LE, /*< reversed bitorder version of AV_CRC_16_ANSI */ + AV_CRC_24_IEEE, + AV_CRC_8_EBU, + AV_CRC_MAX, /*< Not part of public API! Do not use outside libavutil. */ +}AVCRCId; + +/** + * Initialize a CRC table. + * @param ctx must be an array of size sizeof(AVCRC)*257 or sizeof(AVCRC)*1024 + * @param le If 1, the lowest bit represents the coefficient for the highest + * exponent of the corresponding polynomial (both for poly and + * actual CRC). + * If 0, you must swap the CRC parameter and the result of av_crc + * if you need the standard representation (can be simplified in + * most cases to e.g. bswap16): + * av_bswap32(crc << (32-bits)) + * @param bits number of bits for the CRC + * @param poly generator polynomial without the x**bits coefficient, in the + * representation as specified by le + * @param ctx_size size of ctx in bytes + * @return <0 on failure + */ +int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size); + +/** + * Get an initialized standard CRC table. + * @param crc_id ID of a standard CRC + * @return a pointer to the CRC table or NULL on failure + */ +const AVCRC *av_crc_get_table(AVCRCId crc_id); + +/** + * Calculate the CRC of a block. + * @param crc CRC of previous blocks if any or initial value for CRC + * @return CRC updated with the data from the given block + * + * @see av_crc_init() "le" parameter + */ +uint32_t av_crc(const AVCRC *ctx, uint32_t crc, + const uint8_t *buffer, size_t length) av_pure; + +/** + * @} + */ + +#endif /* AVUTIL_CRC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/des.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/des.h new file mode 100644 index 0000000..4cf11f5 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/des.h
@@ -0,0 +1,77 @@ +/* + * DES encryption/decryption + * Copyright (c) 2007 Reimar Doeffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_DES_H +#define AVUTIL_DES_H + +#include <stdint.h> + +/** + * @defgroup lavu_des DES + * @ingroup lavu_crypto + * @{ + */ + +typedef struct AVDES { + uint64_t round_keys[3][16]; + int triple_des; +} AVDES; + +/** + * Allocate an AVDES context. + */ +AVDES *av_des_alloc(void); + +/** + * @brief Initializes an AVDES context. + * + * @param key_bits must be 64 or 192 + * @param decrypt 0 for encryption/CBC-MAC, 1 for decryption + * @return zero on success, negative value otherwise + */ +int av_des_init(struct AVDES *d, const uint8_t *key, int key_bits, int decrypt); + +/** + * @brief Encrypts / decrypts using the DES algorithm. + * + * @param count number of 8 byte blocks + * @param dst destination array, can be equal to src, must be 8-byte aligned + * @param src source array, can be equal to dst, must be 8-byte aligned, may be NULL + * @param iv initialization vector for CBC mode, if NULL then ECB will be used, + * must be 8-byte aligned + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_des_crypt(struct AVDES *d, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt); + +/** + * @brief Calculates CBC-MAC using the DES algorithm. + * + * @param count number of 8 byte blocks + * @param dst destination array, can be equal to src, must be 8-byte aligned + * @param src source array, can be equal to dst, must be 8-byte aligned, may be NULL + */ +void av_des_mac(struct AVDES *d, uint8_t *dst, const uint8_t *src, int count); + +/** + * @} + */ + +#endif /* AVUTIL_DES_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/dict.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/dict.h new file mode 100644 index 0000000..118f1f0 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/dict.h
@@ -0,0 +1,200 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Public dictionary API. + * @deprecated + * AVDictionary is provided for compatibility with libav. It is both in + * implementation as well as API inefficient. It does not scale and is + * extremely slow with large dictionaries. + * It is recommended that new code uses our tree container from tree.c/h + * where applicable, which uses AVL trees to achieve O(log n) performance. + */ + +#ifndef AVUTIL_DICT_H +#define AVUTIL_DICT_H + +#include <stdint.h> + +#include "version.h" + +/** + * @addtogroup lavu_dict AVDictionary + * @ingroup lavu_data + * + * @brief Simple key:value store + * + * @{ + * Dictionaries are used for storing key:value pairs. To create + * an AVDictionary, simply pass an address of a NULL pointer to + * av_dict_set(). NULL can be used as an empty dictionary wherever + * a pointer to an AVDictionary is required. + * Use av_dict_get() to retrieve an entry or iterate over all + * entries and finally av_dict_free() to free the dictionary + * and all its contents. + * + @code + AVDictionary *d = NULL; // "create" an empty dictionary + AVDictionaryEntry *t = NULL; + + av_dict_set(&d, "foo", "bar", 0); // add an entry + + char *k = av_strdup("key"); // if your strings are already allocated, + char *v = av_strdup("value"); // you can avoid copying them like this + av_dict_set(&d, k, v, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); + + while (t = av_dict_get(d, "", t, AV_DICT_IGNORE_SUFFIX)) { + <....> // iterate over all entries in d + } + av_dict_free(&d); + @endcode + */ + +#define AV_DICT_MATCH_CASE 1 /**< Only get an entry with exact-case key match. Only relevant in av_dict_get(). */ +#define AV_DICT_IGNORE_SUFFIX 2 /**< Return first entry in a dictionary whose first part corresponds to the search key, + ignoring the suffix of the found key string. Only relevant in av_dict_get(). */ +#define AV_DICT_DONT_STRDUP_KEY 4 /**< Take ownership of a key that's been + allocated with av_malloc() or another memory allocation function. */ +#define AV_DICT_DONT_STRDUP_VAL 8 /**< Take ownership of a value that's been + allocated with av_malloc() or another memory allocation function. */ +#define AV_DICT_DONT_OVERWRITE 16 ///< Don't overwrite existing entries. +#define AV_DICT_APPEND 32 /**< If the entry already exists, append to it. Note that no + delimiter is added, the strings are simply concatenated. */ +#define AV_DICT_MULTIKEY 64 /**< Allow to store several equal keys in the dictionary */ + +typedef struct AVDictionaryEntry { + char *key; + char *value; +} AVDictionaryEntry; + +typedef struct AVDictionary AVDictionary; + +/** + * Get a dictionary entry with matching key. + * + * The returned entry key or value must not be changed, or it will + * cause undefined behavior. + * + * To iterate through all the dictionary entries, you can set the matching key + * to the null string "" and set the AV_DICT_IGNORE_SUFFIX flag. + * + * @param prev Set to the previous matching element to find the next. + * If set to NULL the first matching element is returned. + * @param key matching key + * @param flags a collection of AV_DICT_* flags controlling how the entry is retrieved + * @return found entry or NULL in case no matching entry was found in the dictionary + */ +AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key, + const AVDictionaryEntry *prev, int flags); + +/** + * Get number of entries in dictionary. + * + * @param m dictionary + * @return number of entries in dictionary + */ +int av_dict_count(const AVDictionary *m); + +/** + * Set the given entry in *pm, overwriting an existing entry. + * + * Note: If AV_DICT_DONT_STRDUP_KEY or AV_DICT_DONT_STRDUP_VAL is set, + * these arguments will be freed on error. + * + * Warning: Adding a new entry to a dictionary invalidates all existing entries + * previously returned with av_dict_get. + * + * @param pm pointer to a pointer to a dictionary struct. If *pm is NULL + * a dictionary struct is allocated and put in *pm. + * @param key entry key to add to *pm (will either be av_strduped or added as a new key depending on flags) + * @param value entry value to add to *pm (will be av_strduped or added as a new key depending on flags). + * Passing a NULL value will cause an existing entry to be deleted. + * @return >= 0 on success otherwise an error code <0 + */ +int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags); + +/** + * Convenience wrapper for av_dict_set that converts the value to a string + * and stores it. + * + * Note: If AV_DICT_DONT_STRDUP_KEY is set, key will be freed on error. + */ +int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags); + +/** + * Parse the key/value pairs list and add the parsed entries to a dictionary. + * + * In case of failure, all the successfully set entries are stored in + * *pm. You may need to manually free the created dictionary. + * + * @param key_val_sep a 0-terminated list of characters used to separate + * key from value + * @param pairs_sep a 0-terminated list of characters used to separate + * two pairs from each other + * @param flags flags to use when adding to dictionary. + * AV_DICT_DONT_STRDUP_KEY and AV_DICT_DONT_STRDUP_VAL + * are ignored since the key/value tokens will always + * be duplicated. + * @return 0 on success, negative AVERROR code on failure + */ +int av_dict_parse_string(AVDictionary **pm, const char *str, + const char *key_val_sep, const char *pairs_sep, + int flags); + +/** + * Copy entries from one AVDictionary struct into another. + * @param dst pointer to a pointer to a AVDictionary struct. If *dst is NULL, + * this function will allocate a struct for you and put it in *dst + * @param src pointer to source AVDictionary struct + * @param flags flags to use when setting entries in *dst + * @note metadata is read using the AV_DICT_IGNORE_SUFFIX flag + * @return 0 on success, negative AVERROR code on failure. If dst was allocated + * by this function, callers should free the associated memory. + */ +int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags); + +/** + * Free all the memory allocated for an AVDictionary struct + * and all keys and values. + */ +void av_dict_free(AVDictionary **m); + +/** + * Get dictionary entries as a string. + * + * Create a string containing dictionary's entries. + * Such string may be passed back to av_dict_parse_string(). + * @note String is escaped with backslashes ('\'). + * + * @param[in] m dictionary + * @param[out] buffer Pointer to buffer that will be allocated with string containg entries. + * Buffer must be freed by the caller when is no longer needed. + * @param[in] key_val_sep character used to separate key from value + * @param[in] pairs_sep character used to separate two pairs from each other + * @return >= 0 on success, negative on error + * @warning Separators cannot be neither '\\' nor '\0'. They also cannot be the same. + */ +int av_dict_get_string(const AVDictionary *m, char **buffer, + const char key_val_sep, const char pairs_sep); + +/** + * @} + */ + +#endif /* AVUTIL_DICT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/display.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/display.h new file mode 100644 index 0000000..515adad --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/display.h
@@ -0,0 +1,114 @@ +/* + * Copyright (c) 2014 Vittorio Giovara <vittorio.giovara@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Display matrix + */ + +#ifndef AVUTIL_DISPLAY_H +#define AVUTIL_DISPLAY_H + +#include <stdint.h> +#include "common.h" + +/** + * @addtogroup lavu_video + * @{ + * + * @defgroup lavu_video_display Display transformation matrix functions + * @{ + */ + +/** + * @addtogroup lavu_video_display + * The display transformation matrix specifies an affine transformation that + * should be applied to video frames for correct presentation. It is compatible + * with the matrices stored in the ISO/IEC 14496-12 container format. + * + * The data is a 3x3 matrix represented as a 9-element array: + * + * @code{.unparsed} + * | a b u | + * (a, b, u, c, d, v, x, y, w) -> | c d v | + * | x y w | + * @endcode + * + * All numbers are stored in native endianness, as 16.16 fixed-point values, + * except for u, v and w, which are stored as 2.30 fixed-point values. + * + * The transformation maps a point (p, q) in the source (pre-transformation) + * frame to the point (p', q') in the destination (post-transformation) frame as + * follows: + * + * @code{.unparsed} + * | a b u | + * (p, q, 1) . | c d v | = z * (p', q', 1) + * | x y w | + * @endcode + * + * The transformation can also be more explicitly written in components as + * follows: + * + * @code{.unparsed} + * p' = (a * p + c * q + x) / z; + * q' = (b * p + d * q + y) / z; + * z = u * p + v * q + w + * @endcode + */ + +/** + * Extract the rotation component of the transformation matrix. + * + * @param matrix the transformation matrix + * @return the angle (in degrees) by which the transformation rotates the frame + * counterclockwise. The angle will be in range [-180.0, 180.0], + * or NaN if the matrix is singular. + * + * @note floating point numbers are inherently inexact, so callers are + * recommended to round the return value to nearest integer before use. + */ +double av_display_rotation_get(const int32_t matrix[9]); + +/** + * Initialize a transformation matrix describing a pure counterclockwise + * rotation by the specified angle (in degrees). + * + * @param matrix an allocated transformation matrix (will be fully overwritten + * by this function) + * @param angle rotation angle in degrees. + */ +void av_display_rotation_set(int32_t matrix[9], double angle); + +/** + * Flip the input matrix horizontally and/or vertically. + * + * @param matrix an allocated transformation matrix + * @param hflip whether the matrix should be flipped horizontally + * @param vflip whether the matrix should be flipped vertically + */ +void av_display_matrix_flip(int32_t matrix[9], int hflip, int vflip); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_DISPLAY_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/downmix_info.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/downmix_info.h new file mode 100644 index 0000000..221cf5b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/downmix_info.h
@@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014 Tim Walker <tdskywalker@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_DOWNMIX_INFO_H +#define AVUTIL_DOWNMIX_INFO_H + +#include "frame.h" + +/** + * @file + * audio downmix medatata + */ + +/** + * @addtogroup lavu_audio + * @{ + */ + +/** + * @defgroup downmix_info Audio downmix metadata + * @{ + */ + +/** + * Possible downmix types. + */ +enum AVDownmixType { + AV_DOWNMIX_TYPE_UNKNOWN, /**< Not indicated. */ + AV_DOWNMIX_TYPE_LORO, /**< Lo/Ro 2-channel downmix (Stereo). */ + AV_DOWNMIX_TYPE_LTRT, /**< Lt/Rt 2-channel downmix, Dolby Surround compatible. */ + AV_DOWNMIX_TYPE_DPLII, /**< Lt/Rt 2-channel downmix, Dolby Pro Logic II compatible. */ + AV_DOWNMIX_TYPE_NB /**< Number of downmix types. Not part of ABI. */ +}; + +/** + * This structure describes optional metadata relevant to a downmix procedure. + * + * All fields are set by the decoder to the value indicated in the audio + * bitstream (if present), or to a "sane" default otherwise. + */ +typedef struct AVDownmixInfo { + /** + * Type of downmix preferred by the mastering engineer. + */ + enum AVDownmixType preferred_downmix_type; + + /** + * Absolute scale factor representing the nominal level of the center + * channel during a regular downmix. + */ + double center_mix_level; + + /** + * Absolute scale factor representing the nominal level of the center + * channel during an Lt/Rt compatible downmix. + */ + double center_mix_level_ltrt; + + /** + * Absolute scale factor representing the nominal level of the surround + * channels during a regular downmix. + */ + double surround_mix_level; + + /** + * Absolute scale factor representing the nominal level of the surround + * channels during an Lt/Rt compatible downmix. + */ + double surround_mix_level_ltrt; + + /** + * Absolute scale factor representing the level at which the LFE data is + * mixed into L/R channels during downmixing. + */ + double lfe_mix_level; +} AVDownmixInfo; + +/** + * Get a frame's AV_FRAME_DATA_DOWNMIX_INFO side data for editing. + * + * If the side data is absent, it is created and added to the frame. + * + * @param frame the frame for which the side data is to be obtained or created + * + * @return the AVDownmixInfo structure to be edited by the caller, or NULL if + * the structure cannot be allocated. + */ +AVDownmixInfo *av_downmix_info_update_side_data(AVFrame *frame); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* AVUTIL_DOWNMIX_INFO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/encryption_info.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/encryption_info.h new file mode 100644 index 0000000..8fe7ebf --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/encryption_info.h
@@ -0,0 +1,205 @@ +/** + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_ENCRYPTION_INFO_H +#define AVUTIL_ENCRYPTION_INFO_H + +#include <stddef.h> +#include <stdint.h> + +typedef struct AVSubsampleEncryptionInfo { + /** The number of bytes that are clear. */ + unsigned int bytes_of_clear_data; + + /** + * The number of bytes that are protected. If using pattern encryption, + * the pattern applies to only the protected bytes; if not using pattern + * encryption, all these bytes are encrypted. + */ + unsigned int bytes_of_protected_data; +} AVSubsampleEncryptionInfo; + +/** + * This describes encryption info for a packet. This contains frame-specific + * info for how to decrypt the packet before passing it to the decoder. + * + * The size of this struct is not part of the public ABI. + */ +typedef struct AVEncryptionInfo { + /** The fourcc encryption scheme, in big-endian byte order. */ + uint32_t scheme; + + /** + * Only used for pattern encryption. This is the number of 16-byte blocks + * that are encrypted. + */ + uint32_t crypt_byte_block; + + /** + * Only used for pattern encryption. This is the number of 16-byte blocks + * that are clear. + */ + uint32_t skip_byte_block; + + /** + * The ID of the key used to encrypt the packet. This should always be + * 16 bytes long, but may be changed in the future. + */ + uint8_t *key_id; + uint32_t key_id_size; + + /** + * The initialization vector. This may have been zero-filled to be the + * correct block size. This should always be 16 bytes long, but may be + * changed in the future. + */ + uint8_t *iv; + uint32_t iv_size; + + /** + * An array of subsample encryption info specifying how parts of the sample + * are encrypted. If there are no subsamples, then the whole sample is + * encrypted. + */ + AVSubsampleEncryptionInfo *subsamples; + uint32_t subsample_count; +} AVEncryptionInfo; + +/** + * This describes info used to initialize an encryption key system. + * + * The size of this struct is not part of the public ABI. + */ +typedef struct AVEncryptionInitInfo { + /** + * A unique identifier for the key system this is for, can be NULL if it + * is not known. This should always be 16 bytes, but may change in the + * future. + */ + uint8_t* system_id; + uint32_t system_id_size; + + /** + * An array of key IDs this initialization data is for. All IDs are the + * same length. Can be NULL if there are no known key IDs. + */ + uint8_t** key_ids; + /** The number of key IDs. */ + uint32_t num_key_ids; + /** + * The number of bytes in each key ID. This should always be 16, but may + * change in the future. + */ + uint32_t key_id_size; + + /** + * Key-system specific initialization data. This data is copied directly + * from the file and the format depends on the specific key system. This + * can be NULL if there is no initialization data; in that case, there + * will be at least one key ID. + */ + uint8_t* data; + uint32_t data_size; + + /** + * An optional pointer to the next initialization info in the list. + */ + struct AVEncryptionInitInfo *next; +} AVEncryptionInitInfo; + +/** + * Allocates an AVEncryptionInfo structure and sub-pointers to hold the given + * number of subsamples. This will allocate pointers for the key ID, IV, + * and subsample entries, set the size members, and zero-initialize the rest. + * + * @param subsample_count The number of subsamples. + * @param key_id_size The number of bytes in the key ID, should be 16. + * @param iv_size The number of bytes in the IV, should be 16. + * + * @return The new AVEncryptionInfo structure, or NULL on error. + */ +AVEncryptionInfo *av_encryption_info_alloc(uint32_t subsample_count, uint32_t key_id_size, uint32_t iv_size); + +/** + * Allocates an AVEncryptionInfo structure with a copy of the given data. + * @return The new AVEncryptionInfo structure, or NULL on error. + */ +AVEncryptionInfo *av_encryption_info_clone(const AVEncryptionInfo *info); + +/** + * Frees the given encryption info object. This MUST NOT be used to free the + * side-data data pointer, that should use normal side-data methods. + */ +void av_encryption_info_free(AVEncryptionInfo *info); + +/** + * Creates a copy of the AVEncryptionInfo that is contained in the given side + * data. The resulting object should be passed to av_encryption_info_free() + * when done. + * + * @return The new AVEncryptionInfo structure, or NULL on error. + */ +AVEncryptionInfo *av_encryption_info_get_side_data(const uint8_t *side_data, size_t side_data_size); + +/** + * Allocates and initializes side data that holds a copy of the given encryption + * info. The resulting pointer should be either freed using av_free or given + * to av_packet_add_side_data(). + * + * @return The new side-data pointer, or NULL. + */ +uint8_t *av_encryption_info_add_side_data( + const AVEncryptionInfo *info, size_t *side_data_size); + + +/** + * Allocates an AVEncryptionInitInfo structure and sub-pointers to hold the + * given sizes. This will allocate pointers and set all the fields. + * + * @return The new AVEncryptionInitInfo structure, or NULL on error. + */ +AVEncryptionInitInfo *av_encryption_init_info_alloc( + uint32_t system_id_size, uint32_t num_key_ids, uint32_t key_id_size, uint32_t data_size); + +/** + * Frees the given encryption init info object. This MUST NOT be used to free + * the side-data data pointer, that should use normal side-data methods. + */ +void av_encryption_init_info_free(AVEncryptionInitInfo* info); + +/** + * Creates a copy of the AVEncryptionInitInfo that is contained in the given + * side data. The resulting object should be passed to + * av_encryption_init_info_free() when done. + * + * @return The new AVEncryptionInitInfo structure, or NULL on error. + */ +AVEncryptionInitInfo *av_encryption_init_info_get_side_data( + const uint8_t* side_data, size_t side_data_size); + +/** + * Allocates and initializes side data that holds a copy of the given encryption + * init info. The resulting pointer should be either freed using av_free or + * given to av_packet_add_side_data(). + * + * @return The new side-data pointer, or NULL. + */ +uint8_t *av_encryption_init_info_add_side_data( + const AVEncryptionInitInfo *info, size_t *side_data_size); + +#endif /* AVUTIL_ENCRYPTION_INFO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/error.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/error.h new file mode 100644 index 0000000..71df4da --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/error.h
@@ -0,0 +1,126 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * error code definitions + */ + +#ifndef AVUTIL_ERROR_H +#define AVUTIL_ERROR_H + +#include <errno.h> +#include <stddef.h> + +/** + * @addtogroup lavu_error + * + * @{ + */ + + +/* error handling */ +#if EDOM > 0 +#define AVERROR(e) (-(e)) ///< Returns a negative error code from a POSIX error code, to return from library functions. +#define AVUNERROR(e) (-(e)) ///< Returns a POSIX error code from a library function error return value. +#else +/* Some platforms have E* and errno already negated. */ +#define AVERROR(e) (e) +#define AVUNERROR(e) (e) +#endif + +#define FFERRTAG(a, b, c, d) (-(int)MKTAG(a, b, c, d)) + +#define AVERROR_BSF_NOT_FOUND FFERRTAG(0xF8,'B','S','F') ///< Bitstream filter not found +#define AVERROR_BUG FFERRTAG( 'B','U','G','!') ///< Internal bug, also see AVERROR_BUG2 +#define AVERROR_BUFFER_TOO_SMALL FFERRTAG( 'B','U','F','S') ///< Buffer too small +#define AVERROR_DECODER_NOT_FOUND FFERRTAG(0xF8,'D','E','C') ///< Decoder not found +#define AVERROR_DEMUXER_NOT_FOUND FFERRTAG(0xF8,'D','E','M') ///< Demuxer not found +#define AVERROR_ENCODER_NOT_FOUND FFERRTAG(0xF8,'E','N','C') ///< Encoder not found +#define AVERROR_EOF FFERRTAG( 'E','O','F',' ') ///< End of file +#define AVERROR_EXIT FFERRTAG( 'E','X','I','T') ///< Immediate exit was requested; the called function should not be restarted +#define AVERROR_EXTERNAL FFERRTAG( 'E','X','T',' ') ///< Generic error in an external library +#define AVERROR_FILTER_NOT_FOUND FFERRTAG(0xF8,'F','I','L') ///< Filter not found +#define AVERROR_INVALIDDATA FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input +#define AVERROR_MUXER_NOT_FOUND FFERRTAG(0xF8,'M','U','X') ///< Muxer not found +#define AVERROR_OPTION_NOT_FOUND FFERRTAG(0xF8,'O','P','T') ///< Option not found +#define AVERROR_PATCHWELCOME FFERRTAG( 'P','A','W','E') ///< Not yet implemented in FFmpeg, patches welcome +#define AVERROR_PROTOCOL_NOT_FOUND FFERRTAG(0xF8,'P','R','O') ///< Protocol not found + +#define AVERROR_STREAM_NOT_FOUND FFERRTAG(0xF8,'S','T','R') ///< Stream not found +/** + * This is semantically identical to AVERROR_BUG + * it has been introduced in Libav after our AVERROR_BUG and with a modified value. + */ +#define AVERROR_BUG2 FFERRTAG( 'B','U','G',' ') +#define AVERROR_UNKNOWN FFERRTAG( 'U','N','K','N') ///< Unknown error, typically from an external library +#define AVERROR_EXPERIMENTAL (-0x2bb2afa8) ///< Requested feature is flagged experimental. Set strict_std_compliance if you really want to use it. +#define AVERROR_INPUT_CHANGED (-0x636e6701) ///< Input changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_OUTPUT_CHANGED) +#define AVERROR_OUTPUT_CHANGED (-0x636e6702) ///< Output changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_INPUT_CHANGED) +/* HTTP & RTSP errors */ +#define AVERROR_HTTP_BAD_REQUEST FFERRTAG(0xF8,'4','0','0') +#define AVERROR_HTTP_UNAUTHORIZED FFERRTAG(0xF8,'4','0','1') +#define AVERROR_HTTP_FORBIDDEN FFERRTAG(0xF8,'4','0','3') +#define AVERROR_HTTP_NOT_FOUND FFERRTAG(0xF8,'4','0','4') +#define AVERROR_HTTP_OTHER_4XX FFERRTAG(0xF8,'4','X','X') +#define AVERROR_HTTP_SERVER_ERROR FFERRTAG(0xF8,'5','X','X') + +#define AV_ERROR_MAX_STRING_SIZE 64 + +/** + * Put a description of the AVERROR code errnum in errbuf. + * In case of failure the global variable errno is set to indicate the + * error. Even in case of failure av_strerror() will print a generic + * error message indicating the errnum provided to errbuf. + * + * @param errnum error code to describe + * @param errbuf buffer to which description is written + * @param errbuf_size the size in bytes of errbuf + * @return 0 on success, a negative value if a description for errnum + * cannot be found + */ +int av_strerror(int errnum, char *errbuf, size_t errbuf_size); + +/** + * Fill the provided buffer with a string containing an error string + * corresponding to the AVERROR code errnum. + * + * @param errbuf a buffer + * @param errbuf_size size in bytes of errbuf + * @param errnum error code to describe + * @return the buffer in input, filled with the error description + * @see av_strerror() + */ +static inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum) +{ + av_strerror(errnum, errbuf, errbuf_size); + return errbuf; +} + +/** + * Convenience macro, the return value should be used only directly in + * function arguments but never stand-alone. + */ +#define av_err2str(errnum) \ + av_make_error_string((char[AV_ERROR_MAX_STRING_SIZE]){0}, AV_ERROR_MAX_STRING_SIZE, errnum) + +/** + * @} + */ + +#endif /* AVUTIL_ERROR_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/eval.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/eval.h new file mode 100644 index 0000000..dacd22b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/eval.h
@@ -0,0 +1,113 @@ +/* + * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * simple arithmetic expression evaluator + */ + +#ifndef AVUTIL_EVAL_H +#define AVUTIL_EVAL_H + +#include "avutil.h" + +typedef struct AVExpr AVExpr; + +/** + * Parse and evaluate an expression. + * Note, this is significantly slower than av_expr_eval(). + * + * @param res a pointer to a double where is put the result value of + * the expression, or NAN in case of error + * @param s expression as a zero terminated string, for example "1+2^3+5*5+sin(2/3)" + * @param const_names NULL terminated array of zero terminated strings of constant identifiers, for example {"PI", "E", 0} + * @param const_values a zero terminated array of values for the identifiers from const_names + * @param func1_names NULL terminated array of zero terminated strings of funcs1 identifiers + * @param funcs1 NULL terminated array of function pointers for functions which take 1 argument + * @param func2_names NULL terminated array of zero terminated strings of funcs2 identifiers + * @param funcs2 NULL terminated array of function pointers for functions which take 2 arguments + * @param opaque a pointer which will be passed to all functions from funcs1 and funcs2 + * @param log_ctx parent logging context + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code otherwise + */ +int av_expr_parse_and_eval(double *res, const char *s, + const char * const *const_names, const double *const_values, + const char * const *func1_names, double (* const *funcs1)(void *, double), + const char * const *func2_names, double (* const *funcs2)(void *, double, double), + void *opaque, int log_offset, void *log_ctx); + +/** + * Parse an expression. + * + * @param expr a pointer where is put an AVExpr containing the parsed + * value in case of successful parsing, or NULL otherwise. + * The pointed to AVExpr must be freed with av_expr_free() by the user + * when it is not needed anymore. + * @param s expression as a zero terminated string, for example "1+2^3+5*5+sin(2/3)" + * @param const_names NULL terminated array of zero terminated strings of constant identifiers, for example {"PI", "E", 0} + * @param func1_names NULL terminated array of zero terminated strings of funcs1 identifiers + * @param funcs1 NULL terminated array of function pointers for functions which take 1 argument + * @param func2_names NULL terminated array of zero terminated strings of funcs2 identifiers + * @param funcs2 NULL terminated array of function pointers for functions which take 2 arguments + * @param log_ctx parent logging context + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code otherwise + */ +int av_expr_parse(AVExpr **expr, const char *s, + const char * const *const_names, + const char * const *func1_names, double (* const *funcs1)(void *, double), + const char * const *func2_names, double (* const *funcs2)(void *, double, double), + int log_offset, void *log_ctx); + +/** + * Evaluate a previously parsed expression. + * + * @param const_values a zero terminated array of values for the identifiers from av_expr_parse() const_names + * @param opaque a pointer which will be passed to all functions from funcs1 and funcs2 + * @return the value of the expression + */ +double av_expr_eval(AVExpr *e, const double *const_values, void *opaque); + +/** + * Free a parsed expression previously created with av_expr_parse(). + */ +void av_expr_free(AVExpr *e); + +/** + * Parse the string in numstr and return its value as a double. If + * the string is empty, contains only whitespaces, or does not contain + * an initial substring that has the expected syntax for a + * floating-point number, no conversion is performed. In this case, + * returns a value of zero and the value returned in tail is the value + * of numstr. + * + * @param numstr a string representing a number, may contain one of + * the International System number postfixes, for example 'K', 'M', + * 'G'. If 'i' is appended after the postfix, powers of 2 are used + * instead of powers of 10. The 'B' postfix multiplies the value by + * 8, and can be appended after another postfix or used alone. This + * allows using for example 'KB', 'MiB', 'G' and 'B' as postfix. + * @param tail if non-NULL puts here the pointer to the char next + * after the last parsed character + */ +double av_strtod(const char *numstr, char **tail); + +#endif /* AVUTIL_EVAL_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ffversion.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ffversion.h new file mode 100644 index 0000000..c5bec5d --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ffversion.h
@@ -0,0 +1,5 @@ +/* Automatically generated by version.sh, do not manually edit! */ +#ifndef AVUTIL_FFVERSION_H +#define AVUTIL_FFVERSION_H +#define FFMPEG_VERSION "4.1.3-1+build1" +#endif /* AVUTIL_FFVERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/fifo.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/fifo.h new file mode 100644 index 0000000..dc7bc6f --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/fifo.h
@@ -0,0 +1,179 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * a very simple circular buffer FIFO implementation + */ + +#ifndef AVUTIL_FIFO_H +#define AVUTIL_FIFO_H + +#include <stdint.h> +#include "avutil.h" +#include "attributes.h" + +typedef struct AVFifoBuffer { + uint8_t *buffer; + uint8_t *rptr, *wptr, *end; + uint32_t rndx, wndx; +} AVFifoBuffer; + +/** + * Initialize an AVFifoBuffer. + * @param size of FIFO + * @return AVFifoBuffer or NULL in case of memory allocation failure + */ +AVFifoBuffer *av_fifo_alloc(unsigned int size); + +/** + * Initialize an AVFifoBuffer. + * @param nmemb number of elements + * @param size size of the single element + * @return AVFifoBuffer or NULL in case of memory allocation failure + */ +AVFifoBuffer *av_fifo_alloc_array(size_t nmemb, size_t size); + +/** + * Free an AVFifoBuffer. + * @param f AVFifoBuffer to free + */ +void av_fifo_free(AVFifoBuffer *f); + +/** + * Free an AVFifoBuffer and reset pointer to NULL. + * @param f AVFifoBuffer to free + */ +void av_fifo_freep(AVFifoBuffer **f); + +/** + * Reset the AVFifoBuffer to the state right after av_fifo_alloc, in particular it is emptied. + * @param f AVFifoBuffer to reset + */ +void av_fifo_reset(AVFifoBuffer *f); + +/** + * Return the amount of data in bytes in the AVFifoBuffer, that is the + * amount of data you can read from it. + * @param f AVFifoBuffer to read from + * @return size + */ +int av_fifo_size(const AVFifoBuffer *f); + +/** + * Return the amount of space in bytes in the AVFifoBuffer, that is the + * amount of data you can write into it. + * @param f AVFifoBuffer to write into + * @return size + */ +int av_fifo_space(const AVFifoBuffer *f); + +/** + * Feed data at specific position from an AVFifoBuffer to a user-supplied callback. + * Similar as av_fifo_gereric_read but without discarding data. + * @param f AVFifoBuffer to read from + * @param offset offset from current read position + * @param buf_size number of bytes to read + * @param func generic read function + * @param dest data destination + */ +int av_fifo_generic_peek_at(AVFifoBuffer *f, void *dest, int offset, int buf_size, void (*func)(void*, void*, int)); + +/** + * Feed data from an AVFifoBuffer to a user-supplied callback. + * Similar as av_fifo_gereric_read but without discarding data. + * @param f AVFifoBuffer to read from + * @param buf_size number of bytes to read + * @param func generic read function + * @param dest data destination + */ +int av_fifo_generic_peek(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int)); + +/** + * Feed data from an AVFifoBuffer to a user-supplied callback. + * @param f AVFifoBuffer to read from + * @param buf_size number of bytes to read + * @param func generic read function + * @param dest data destination + */ +int av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int)); + +/** + * Feed data from a user-supplied callback to an AVFifoBuffer. + * @param f AVFifoBuffer to write to + * @param src data source; non-const since it may be used as a + * modifiable context by the function defined in func + * @param size number of bytes to write + * @param func generic write function; the first parameter is src, + * the second is dest_buf, the third is dest_buf_size. + * func must return the number of bytes written to dest_buf, or <= 0 to + * indicate no more data available to write. + * If func is NULL, src is interpreted as a simple byte array for source data. + * @return the number of bytes written to the FIFO + */ +int av_fifo_generic_write(AVFifoBuffer *f, void *src, int size, int (*func)(void*, void*, int)); + +/** + * Resize an AVFifoBuffer. + * In case of reallocation failure, the old FIFO is kept unchanged. + * + * @param f AVFifoBuffer to resize + * @param size new AVFifoBuffer size in bytes + * @return <0 for failure, >=0 otherwise + */ +int av_fifo_realloc2(AVFifoBuffer *f, unsigned int size); + +/** + * Enlarge an AVFifoBuffer. + * In case of reallocation failure, the old FIFO is kept unchanged. + * The new fifo size may be larger than the requested size. + * + * @param f AVFifoBuffer to resize + * @param additional_space the amount of space in bytes to allocate in addition to av_fifo_size() + * @return <0 for failure, >=0 otherwise + */ +int av_fifo_grow(AVFifoBuffer *f, unsigned int additional_space); + +/** + * Read and discard the specified amount of data from an AVFifoBuffer. + * @param f AVFifoBuffer to read from + * @param size amount of data to read in bytes + */ +void av_fifo_drain(AVFifoBuffer *f, int size); + +/** + * Return a pointer to the data stored in a FIFO buffer at a certain offset. + * The FIFO buffer is not modified. + * + * @param f AVFifoBuffer to peek at, f must be non-NULL + * @param offs an offset in bytes, its absolute value must be less + * than the used buffer size or the returned pointer will + * point outside to the buffer data. + * The used buffer size can be checked with av_fifo_size(). + */ +static inline uint8_t *av_fifo_peek2(const AVFifoBuffer *f, int offs) +{ + uint8_t *ptr = f->rptr + offs; + if (ptr >= f->end) + ptr = f->buffer + (ptr - f->end); + else if (ptr < f->buffer) + ptr = f->end - (f->buffer - ptr); + return ptr; +} + +#endif /* AVUTIL_FIFO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/file.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/file.h new file mode 100644 index 0000000..3ef4a60 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/file.h
@@ -0,0 +1,71 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_FILE_H +#define AVUTIL_FILE_H + +#include <stdint.h> + +#include "avutil.h" + +/** + * @file + * Misc file utilities. + */ + +/** + * Read the file with name filename, and put its content in a newly + * allocated buffer or map it with mmap() when available. + * In case of success set *bufptr to the read or mmapped buffer, and + * *size to the size in bytes of the buffer in *bufptr. + * Unlike mmap this function succeeds with zero sized files, in this + * case *bufptr will be set to NULL and *size will be set to 0. + * The returned buffer must be released with av_file_unmap(). + * + * @param log_offset loglevel offset used for logging + * @param log_ctx context used for logging + * @return a non negative number in case of success, a negative value + * corresponding to an AVERROR error code in case of failure + */ +av_warn_unused_result +int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, + int log_offset, void *log_ctx); + +/** + * Unmap or free the buffer bufptr created by av_file_map(). + * + * @param size size in bytes of bufptr, must be the same as returned + * by av_file_map() + */ +void av_file_unmap(uint8_t *bufptr, size_t size); + +/** + * Wrapper to work around the lack of mkstemp() on mingw. + * Also, tries to create file in /tmp first, if possible. + * *prefix can be a character constant; *filename will be allocated internally. + * @return file descriptor of opened file (or negative value corresponding to an + * AVERROR code on error) + * and opened file name in **filename. + * @note On very old libcs it is necessary to set a secure umask before + * calling this, av_tempfile() can't call umask itself as it is used in + * libraries and could interfere with the calling application. + * @deprecated as fd numbers cannot be passed saftely between libs on some platforms + */ +int av_tempfile(const char *prefix, char **filename, int log_offset, void *log_ctx); + +#endif /* AVUTIL_FILE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/frame.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/frame.h new file mode 100644 index 0000000..e2a2929 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/frame.h
@@ -0,0 +1,901 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_frame + * reference-counted frame API + */ + +#ifndef AVUTIL_FRAME_H +#define AVUTIL_FRAME_H + +#include <stddef.h> +#include <stdint.h> + +#include "avutil.h" +#include "buffer.h" +#include "dict.h" +#include "rational.h" +#include "samplefmt.h" +#include "pixfmt.h" +#include "version.h" + + +/** + * @defgroup lavu_frame AVFrame + * @ingroup lavu_data + * + * @{ + * AVFrame is an abstraction for reference-counted raw multimedia data. + */ + +enum AVFrameSideDataType { + /** + * The data is the AVPanScan struct defined in libavcodec. + */ + AV_FRAME_DATA_PANSCAN, + /** + * ATSC A53 Part 4 Closed Captions. + * A53 CC bitstream is stored as uint8_t in AVFrameSideData.data. + * The number of bytes of CC data is AVFrameSideData.size. + */ + AV_FRAME_DATA_A53_CC, + /** + * Stereoscopic 3d metadata. + * The data is the AVStereo3D struct defined in libavutil/stereo3d.h. + */ + AV_FRAME_DATA_STEREO3D, + /** + * The data is the AVMatrixEncoding enum defined in libavutil/channel_layout.h. + */ + AV_FRAME_DATA_MATRIXENCODING, + /** + * Metadata relevant to a downmix procedure. + * The data is the AVDownmixInfo struct defined in libavutil/downmix_info.h. + */ + AV_FRAME_DATA_DOWNMIX_INFO, + /** + * ReplayGain information in the form of the AVReplayGain struct. + */ + AV_FRAME_DATA_REPLAYGAIN, + /** + * This side data contains a 3x3 transformation matrix describing an affine + * transformation that needs to be applied to the frame for correct + * presentation. + * + * See libavutil/display.h for a detailed description of the data. + */ + AV_FRAME_DATA_DISPLAYMATRIX, + /** + * Active Format Description data consisting of a single byte as specified + * in ETSI TS 101 154 using AVActiveFormatDescription enum. + */ + AV_FRAME_DATA_AFD, + /** + * Motion vectors exported by some codecs (on demand through the export_mvs + * flag set in the libavcodec AVCodecContext flags2 option). + * The data is the AVMotionVector struct defined in + * libavutil/motion_vector.h. + */ + AV_FRAME_DATA_MOTION_VECTORS, + /** + * Recommmends skipping the specified number of samples. This is exported + * only if the "skip_manual" AVOption is set in libavcodec. + * This has the same format as AV_PKT_DATA_SKIP_SAMPLES. + * @code + * u32le number of samples to skip from start of this packet + * u32le number of samples to skip from end of this packet + * u8 reason for start skip + * u8 reason for end skip (0=padding silence, 1=convergence) + * @endcode + */ + AV_FRAME_DATA_SKIP_SAMPLES, + /** + * This side data must be associated with an audio frame and corresponds to + * enum AVAudioServiceType defined in avcodec.h. + */ + AV_FRAME_DATA_AUDIO_SERVICE_TYPE, + /** + * Mastering display metadata associated with a video frame. The payload is + * an AVMasteringDisplayMetadata type and contains information about the + * mastering display color volume. + */ + AV_FRAME_DATA_MASTERING_DISPLAY_METADATA, + /** + * The GOP timecode in 25 bit timecode format. Data format is 64-bit integer. + * This is set on the first frame of a GOP that has a temporal reference of 0. + */ + AV_FRAME_DATA_GOP_TIMECODE, + + /** + * The data represents the AVSphericalMapping structure defined in + * libavutil/spherical.h. + */ + AV_FRAME_DATA_SPHERICAL, + + /** + * Content light level (based on CTA-861.3). This payload contains data in + * the form of the AVContentLightMetadata struct. + */ + AV_FRAME_DATA_CONTENT_LIGHT_LEVEL, + + /** + * The data contains an ICC profile as an opaque octet buffer following the + * format described by ISO 15076-1 with an optional name defined in the + * metadata key entry "name". + */ + AV_FRAME_DATA_ICC_PROFILE, + +#if FF_API_FRAME_QP + /** + * Implementation-specific description of the format of AV_FRAME_QP_TABLE_DATA. + * The contents of this side data are undocumented and internal; use + * av_frame_set_qp_table() and av_frame_get_qp_table() to access this in a + * meaningful way instead. + */ + AV_FRAME_DATA_QP_TABLE_PROPERTIES, + + /** + * Raw QP table data. Its format is described by + * AV_FRAME_DATA_QP_TABLE_PROPERTIES. Use av_frame_set_qp_table() and + * av_frame_get_qp_table() to access this instead. + */ + AV_FRAME_DATA_QP_TABLE_DATA, +#endif + + /** + * Timecode which conforms to SMPTE ST 12-1. The data is an array of 4 uint32_t + * where the first uint32_t describes how many (1-3) of the other timecodes are used. + * The timecode format is described in the av_timecode_get_smpte_from_framenum() + * function in libavutil/timecode.c. + */ + AV_FRAME_DATA_S12M_TIMECODE, +}; + +enum AVActiveFormatDescription { + AV_AFD_SAME = 8, + AV_AFD_4_3 = 9, + AV_AFD_16_9 = 10, + AV_AFD_14_9 = 11, + AV_AFD_4_3_SP_14_9 = 13, + AV_AFD_16_9_SP_14_9 = 14, + AV_AFD_SP_4_3 = 15, +}; + + +/** + * Structure to hold side data for an AVFrame. + * + * sizeof(AVFrameSideData) is not a part of the public ABI, so new fields may be added + * to the end with a minor bump. + */ +typedef struct AVFrameSideData { + enum AVFrameSideDataType type; + uint8_t *data; + int size; + AVDictionary *metadata; + AVBufferRef *buf; +} AVFrameSideData; + +/** + * This structure describes decoded (raw) audio or video data. + * + * AVFrame must be allocated using av_frame_alloc(). Note that this only + * allocates the AVFrame itself, the buffers for the data must be managed + * through other means (see below). + * AVFrame must be freed with av_frame_free(). + * + * AVFrame is typically allocated once and then reused multiple times to hold + * different data (e.g. a single AVFrame to hold frames received from a + * decoder). In such a case, av_frame_unref() will free any references held by + * the frame and reset it to its original clean state before it + * is reused again. + * + * The data described by an AVFrame is usually reference counted through the + * AVBuffer API. The underlying buffer references are stored in AVFrame.buf / + * AVFrame.extended_buf. An AVFrame is considered to be reference counted if at + * least one reference is set, i.e. if AVFrame.buf[0] != NULL. In such a case, + * every single data plane must be contained in one of the buffers in + * AVFrame.buf or AVFrame.extended_buf. + * There may be a single buffer for all the data, or one separate buffer for + * each plane, or anything in between. + * + * sizeof(AVFrame) is not a part of the public ABI, so new fields may be added + * to the end with a minor bump. + * + * Fields can be accessed through AVOptions, the name string used, matches the + * C structure field name for fields accessible through AVOptions. The AVClass + * for AVFrame can be obtained from avcodec_get_frame_class() + */ +typedef struct AVFrame { +#define AV_NUM_DATA_POINTERS 8 + /** + * pointer to the picture/channel planes. + * This might be different from the first allocated byte + * + * Some decoders access areas outside 0,0 - width,height, please + * see avcodec_align_dimensions2(). Some filters and swscale can read + * up to 16 bytes beyond the planes, if these filters are to be used, + * then 16 extra bytes must be allocated. + * + * NOTE: Except for hwaccel formats, pointers not needed by the format + * MUST be set to NULL. + */ + uint8_t *data[AV_NUM_DATA_POINTERS]; + + /** + * For video, size in bytes of each picture line. + * For audio, size in bytes of each plane. + * + * For audio, only linesize[0] may be set. For planar audio, each channel + * plane must be the same size. + * + * For video the linesizes should be multiples of the CPUs alignment + * preference, this is 16 or 32 for modern desktop CPUs. + * Some code requires such alignment other code can be slower without + * correct alignment, for yet other it makes no difference. + * + * @note The linesize may be larger than the size of usable data -- there + * may be extra padding present for performance reasons. + */ + int linesize[AV_NUM_DATA_POINTERS]; + + /** + * pointers to the data planes/channels. + * + * For video, this should simply point to data[]. + * + * For planar audio, each channel has a separate data pointer, and + * linesize[0] contains the size of each channel buffer. + * For packed audio, there is just one data pointer, and linesize[0] + * contains the total size of the buffer for all channels. + * + * Note: Both data and extended_data should always be set in a valid frame, + * but for planar audio with more channels that can fit in data, + * extended_data must be used in order to access all channels. + */ + uint8_t **extended_data; + + /** + * @name Video dimensions + * Video frames only. The coded dimensions (in pixels) of the video frame, + * i.e. the size of the rectangle that contains some well-defined values. + * + * @note The part of the frame intended for display/presentation is further + * restricted by the @ref cropping "Cropping rectangle". + * @{ + */ + int width, height; + /** + * @} + */ + + /** + * number of audio samples (per channel) described by this frame + */ + int nb_samples; + + /** + * format of the frame, -1 if unknown or unset + * Values correspond to enum AVPixelFormat for video frames, + * enum AVSampleFormat for audio) + */ + int format; + + /** + * 1 -> keyframe, 0-> not + */ + int key_frame; + + /** + * Picture type of the frame. + */ + enum AVPictureType pict_type; + + /** + * Sample aspect ratio for the video frame, 0/1 if unknown/unspecified. + */ + AVRational sample_aspect_ratio; + + /** + * Presentation timestamp in time_base units (time when frame should be shown to user). + */ + int64_t pts; + +#if FF_API_PKT_PTS + /** + * PTS copied from the AVPacket that was decoded to produce this frame. + * @deprecated use the pts field instead + */ + attribute_deprecated + int64_t pkt_pts; +#endif + + /** + * DTS copied from the AVPacket that triggered returning this frame. (if frame threading isn't used) + * This is also the Presentation time of this AVFrame calculated from + * only AVPacket.dts values without pts values. + */ + int64_t pkt_dts; + + /** + * picture number in bitstream order + */ + int coded_picture_number; + /** + * picture number in display order + */ + int display_picture_number; + + /** + * quality (between 1 (good) and FF_LAMBDA_MAX (bad)) + */ + int quality; + + /** + * for some private data of the user + */ + void *opaque; + +#if FF_API_ERROR_FRAME + /** + * @deprecated unused + */ + attribute_deprecated + uint64_t error[AV_NUM_DATA_POINTERS]; +#endif + + /** + * When decoding, this signals how much the picture must be delayed. + * extra_delay = repeat_pict / (2*fps) + */ + int repeat_pict; + + /** + * The content of the picture is interlaced. + */ + int interlaced_frame; + + /** + * If the content is interlaced, is top field displayed first. + */ + int top_field_first; + + /** + * Tell user application that palette has changed from previous frame. + */ + int palette_has_changed; + + /** + * reordered opaque 64 bits (generally an integer or a double precision float + * PTS but can be anything). + * The user sets AVCodecContext.reordered_opaque to represent the input at + * that time, + * the decoder reorders values as needed and sets AVFrame.reordered_opaque + * to exactly one of the values provided by the user through AVCodecContext.reordered_opaque + * @deprecated in favor of pkt_pts + */ + int64_t reordered_opaque; + + /** + * Sample rate of the audio data. + */ + int sample_rate; + + /** + * Channel layout of the audio data. + */ + uint64_t channel_layout; + + /** + * AVBuffer references backing the data for this frame. If all elements of + * this array are NULL, then this frame is not reference counted. This array + * must be filled contiguously -- if buf[i] is non-NULL then buf[j] must + * also be non-NULL for all j < i. + * + * There may be at most one AVBuffer per data plane, so for video this array + * always contains all the references. For planar audio with more than + * AV_NUM_DATA_POINTERS channels, there may be more buffers than can fit in + * this array. Then the extra AVBufferRef pointers are stored in the + * extended_buf array. + */ + AVBufferRef *buf[AV_NUM_DATA_POINTERS]; + + /** + * For planar audio which requires more than AV_NUM_DATA_POINTERS + * AVBufferRef pointers, this array will hold all the references which + * cannot fit into AVFrame.buf. + * + * Note that this is different from AVFrame.extended_data, which always + * contains all the pointers. This array only contains the extra pointers, + * which cannot fit into AVFrame.buf. + * + * This array is always allocated using av_malloc() by whoever constructs + * the frame. It is freed in av_frame_unref(). + */ + AVBufferRef **extended_buf; + /** + * Number of elements in extended_buf. + */ + int nb_extended_buf; + + AVFrameSideData **side_data; + int nb_side_data; + +/** + * @defgroup lavu_frame_flags AV_FRAME_FLAGS + * @ingroup lavu_frame + * Flags describing additional frame properties. + * + * @{ + */ + +/** + * The frame data may be corrupted, e.g. due to decoding errors. + */ +#define AV_FRAME_FLAG_CORRUPT (1 << 0) +/** + * A flag to mark the frames which need to be decoded, but shouldn't be output. + */ +#define AV_FRAME_FLAG_DISCARD (1 << 2) +/** + * @} + */ + + /** + * Frame flags, a combination of @ref lavu_frame_flags + */ + int flags; + + /** + * MPEG vs JPEG YUV range. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorRange color_range; + + enum AVColorPrimaries color_primaries; + + enum AVColorTransferCharacteristic color_trc; + + /** + * YUV colorspace type. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorSpace colorspace; + + enum AVChromaLocation chroma_location; + + /** + * frame timestamp estimated using various heuristics, in stream time base + * - encoding: unused + * - decoding: set by libavcodec, read by user. + */ + int64_t best_effort_timestamp; + + /** + * reordered pos from the last AVPacket that has been input into the decoder + * - encoding: unused + * - decoding: Read by user. + */ + int64_t pkt_pos; + + /** + * duration of the corresponding packet, expressed in + * AVStream->time_base units, 0 if unknown. + * - encoding: unused + * - decoding: Read by user. + */ + int64_t pkt_duration; + + /** + * metadata. + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + AVDictionary *metadata; + + /** + * decode error flags of the frame, set to a combination of + * FF_DECODE_ERROR_xxx flags if the decoder produced a frame, but there + * were errors during the decoding. + * - encoding: unused + * - decoding: set by libavcodec, read by user. + */ + int decode_error_flags; +#define FF_DECODE_ERROR_INVALID_BITSTREAM 1 +#define FF_DECODE_ERROR_MISSING_REFERENCE 2 + + /** + * number of audio channels, only used for audio. + * - encoding: unused + * - decoding: Read by user. + */ + int channels; + + /** + * size of the corresponding packet containing the compressed + * frame. + * It is set to a negative value if unknown. + * - encoding: unused + * - decoding: set by libavcodec, read by user. + */ + int pkt_size; + +#if FF_API_FRAME_QP + /** + * QP table + */ + attribute_deprecated + int8_t *qscale_table; + /** + * QP store stride + */ + attribute_deprecated + int qstride; + + attribute_deprecated + int qscale_type; + + attribute_deprecated + AVBufferRef *qp_table_buf; +#endif + /** + * For hwaccel-format frames, this should be a reference to the + * AVHWFramesContext describing the frame. + */ + AVBufferRef *hw_frames_ctx; + + /** + * AVBufferRef for free use by the API user. FFmpeg will never check the + * contents of the buffer ref. FFmpeg calls av_buffer_unref() on it when + * the frame is unreferenced. av_frame_copy_props() calls create a new + * reference with av_buffer_ref() for the target frame's opaque_ref field. + * + * This is unrelated to the opaque field, although it serves a similar + * purpose. + */ + AVBufferRef *opaque_ref; + + /** + * @anchor cropping + * @name Cropping + * Video frames only. The number of pixels to discard from the the + * top/bottom/left/right border of the frame to obtain the sub-rectangle of + * the frame intended for presentation. + * @{ + */ + size_t crop_top; + size_t crop_bottom; + size_t crop_left; + size_t crop_right; + /** + * @} + */ + + /** + * AVBufferRef for internal use by a single libav* library. + * Must not be used to transfer data between libraries. + * Has to be NULL when ownership of the frame leaves the respective library. + * + * Code outside the FFmpeg libs should never check or change the contents of the buffer ref. + * + * FFmpeg calls av_buffer_unref() on it when the frame is unreferenced. + * av_frame_copy_props() calls create a new reference with av_buffer_ref() + * for the target frame's private_ref field. + */ + AVBufferRef *private_ref; +} AVFrame; + +#if FF_API_FRAME_GET_SET +/** + * Accessors for some AVFrame fields. These used to be provided for ABI + * compatibility, and do not need to be used anymore. + */ +attribute_deprecated +int64_t av_frame_get_best_effort_timestamp(const AVFrame *frame); +attribute_deprecated +void av_frame_set_best_effort_timestamp(AVFrame *frame, int64_t val); +attribute_deprecated +int64_t av_frame_get_pkt_duration (const AVFrame *frame); +attribute_deprecated +void av_frame_set_pkt_duration (AVFrame *frame, int64_t val); +attribute_deprecated +int64_t av_frame_get_pkt_pos (const AVFrame *frame); +attribute_deprecated +void av_frame_set_pkt_pos (AVFrame *frame, int64_t val); +attribute_deprecated +int64_t av_frame_get_channel_layout (const AVFrame *frame); +attribute_deprecated +void av_frame_set_channel_layout (AVFrame *frame, int64_t val); +attribute_deprecated +int av_frame_get_channels (const AVFrame *frame); +attribute_deprecated +void av_frame_set_channels (AVFrame *frame, int val); +attribute_deprecated +int av_frame_get_sample_rate (const AVFrame *frame); +attribute_deprecated +void av_frame_set_sample_rate (AVFrame *frame, int val); +attribute_deprecated +AVDictionary *av_frame_get_metadata (const AVFrame *frame); +attribute_deprecated +void av_frame_set_metadata (AVFrame *frame, AVDictionary *val); +attribute_deprecated +int av_frame_get_decode_error_flags (const AVFrame *frame); +attribute_deprecated +void av_frame_set_decode_error_flags (AVFrame *frame, int val); +attribute_deprecated +int av_frame_get_pkt_size(const AVFrame *frame); +attribute_deprecated +void av_frame_set_pkt_size(AVFrame *frame, int val); +#if FF_API_FRAME_QP +attribute_deprecated +int8_t *av_frame_get_qp_table(AVFrame *f, int *stride, int *type); +attribute_deprecated +int av_frame_set_qp_table(AVFrame *f, AVBufferRef *buf, int stride, int type); +#endif +attribute_deprecated +enum AVColorSpace av_frame_get_colorspace(const AVFrame *frame); +attribute_deprecated +void av_frame_set_colorspace(AVFrame *frame, enum AVColorSpace val); +attribute_deprecated +enum AVColorRange av_frame_get_color_range(const AVFrame *frame); +attribute_deprecated +void av_frame_set_color_range(AVFrame *frame, enum AVColorRange val); +#endif + +/** + * Get the name of a colorspace. + * @return a static string identifying the colorspace; can be NULL. + */ +const char *av_get_colorspace_name(enum AVColorSpace val); + +/** + * Allocate an AVFrame and set its fields to default values. The resulting + * struct must be freed using av_frame_free(). + * + * @return An AVFrame filled with default values or NULL on failure. + * + * @note this only allocates the AVFrame itself, not the data buffers. Those + * must be allocated through other means, e.g. with av_frame_get_buffer() or + * manually. + */ +AVFrame *av_frame_alloc(void); + +/** + * Free the frame and any dynamically allocated objects in it, + * e.g. extended_data. If the frame is reference counted, it will be + * unreferenced first. + * + * @param frame frame to be freed. The pointer will be set to NULL. + */ +void av_frame_free(AVFrame **frame); + +/** + * Set up a new reference to the data described by the source frame. + * + * Copy frame properties from src to dst and create a new reference for each + * AVBufferRef from src. + * + * If src is not reference counted, new buffers are allocated and the data is + * copied. + * + * @warning: dst MUST have been either unreferenced with av_frame_unref(dst), + * or newly allocated with av_frame_alloc() before calling this + * function, or undefined behavior will occur. + * + * @return 0 on success, a negative AVERROR on error + */ +int av_frame_ref(AVFrame *dst, const AVFrame *src); + +/** + * Create a new frame that references the same data as src. + * + * This is a shortcut for av_frame_alloc()+av_frame_ref(). + * + * @return newly created AVFrame on success, NULL on error. + */ +AVFrame *av_frame_clone(const AVFrame *src); + +/** + * Unreference all the buffers referenced by frame and reset the frame fields. + */ +void av_frame_unref(AVFrame *frame); + +/** + * Move everything contained in src to dst and reset src. + * + * @warning: dst is not unreferenced, but directly overwritten without reading + * or deallocating its contents. Call av_frame_unref(dst) manually + * before calling this function to ensure that no memory is leaked. + */ +void av_frame_move_ref(AVFrame *dst, AVFrame *src); + +/** + * Allocate new buffer(s) for audio or video data. + * + * The following fields must be set on frame before calling this function: + * - format (pixel format for video, sample format for audio) + * - width and height for video + * - nb_samples and channel_layout for audio + * + * This function will fill AVFrame.data and AVFrame.buf arrays and, if + * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf. + * For planar formats, one buffer will be allocated for each plane. + * + * @warning: if frame already has been allocated, calling this function will + * leak memory. In addition, undefined behavior can occur in certain + * cases. + * + * @param frame frame in which to store the new buffers. + * @param align Required buffer size alignment. If equal to 0, alignment will be + * chosen automatically for the current CPU. It is highly + * recommended to pass 0 here unless you know what you are doing. + * + * @return 0 on success, a negative AVERROR on error. + */ +int av_frame_get_buffer(AVFrame *frame, int align); + +/** + * Check if the frame data is writable. + * + * @return A positive value if the frame data is writable (which is true if and + * only if each of the underlying buffers has only one reference, namely the one + * stored in this frame). Return 0 otherwise. + * + * If 1 is returned the answer is valid until av_buffer_ref() is called on any + * of the underlying AVBufferRefs (e.g. through av_frame_ref() or directly). + * + * @see av_frame_make_writable(), av_buffer_is_writable() + */ +int av_frame_is_writable(AVFrame *frame); + +/** + * Ensure that the frame data is writable, avoiding data copy if possible. + * + * Do nothing if the frame is writable, allocate new buffers and copy the data + * if it is not. + * + * @return 0 on success, a negative AVERROR on error. + * + * @see av_frame_is_writable(), av_buffer_is_writable(), + * av_buffer_make_writable() + */ +int av_frame_make_writable(AVFrame *frame); + +/** + * Copy the frame data from src to dst. + * + * This function does not allocate anything, dst must be already initialized and + * allocated with the same parameters as src. + * + * This function only copies the frame data (i.e. the contents of the data / + * extended data arrays), not any other properties. + * + * @return >= 0 on success, a negative AVERROR on error. + */ +int av_frame_copy(AVFrame *dst, const AVFrame *src); + +/** + * Copy only "metadata" fields from src to dst. + * + * Metadata for the purpose of this function are those fields that do not affect + * the data layout in the buffers. E.g. pts, sample rate (for audio) or sample + * aspect ratio (for video), but not width/height or channel layout. + * Side data is also copied. + */ +int av_frame_copy_props(AVFrame *dst, const AVFrame *src); + +/** + * Get the buffer reference a given data plane is stored in. + * + * @param plane index of the data plane of interest in frame->extended_data. + * + * @return the buffer reference that contains the plane or NULL if the input + * frame is not valid. + */ +AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane); + +/** + * Add a new side data to a frame. + * + * @param frame a frame to which the side data should be added + * @param type type of the added side data + * @param size size of the side data + * + * @return newly added side data on success, NULL on error + */ +AVFrameSideData *av_frame_new_side_data(AVFrame *frame, + enum AVFrameSideDataType type, + int size); + +/** + * Add a new side data to a frame from an existing AVBufferRef + * + * @param frame a frame to which the side data should be added + * @param type the type of the added side data + * @param buf an AVBufferRef to add as side data. The ownership of + * the reference is transferred to the frame. + * + * @return newly added side data on success, NULL on error. On failure + * the frame is unchanged and the AVBufferRef remains owned by + * the caller. + */ +AVFrameSideData *av_frame_new_side_data_from_buf(AVFrame *frame, + enum AVFrameSideDataType type, + AVBufferRef *buf); + +/** + * @return a pointer to the side data of a given type on success, NULL if there + * is no side data with such type in this frame. + */ +AVFrameSideData *av_frame_get_side_data(const AVFrame *frame, + enum AVFrameSideDataType type); + +/** + * If side data of the supplied type exists in the frame, free it and remove it + * from the frame. + */ +void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type); + + +/** + * Flags for frame cropping. + */ +enum { + /** + * Apply the maximum possible cropping, even if it requires setting the + * AVFrame.data[] entries to unaligned pointers. Passing unaligned data + * to FFmpeg API is generally not allowed, and causes undefined behavior + * (such as crashes). You can pass unaligned data only to FFmpeg APIs that + * are explicitly documented to accept it. Use this flag only if you + * absolutely know what you are doing. + */ + AV_FRAME_CROP_UNALIGNED = 1 << 0, +}; + +/** + * Crop the given video AVFrame according to its crop_left/crop_top/crop_right/ + * crop_bottom fields. If cropping is successful, the function will adjust the + * data pointers and the width/height fields, and set the crop fields to 0. + * + * In all cases, the cropping boundaries will be rounded to the inherent + * alignment of the pixel format. In some cases, such as for opaque hwaccel + * formats, the left/top cropping is ignored. The crop fields are set to 0 even + * if the cropping was rounded or ignored. + * + * @param frame the frame which should be cropped + * @param flags Some combination of AV_FRAME_CROP_* flags, or 0. + * + * @return >= 0 on success, a negative AVERROR on error. If the cropping fields + * were invalid, AVERROR(ERANGE) is returned, and nothing is changed. + */ +int av_frame_apply_cropping(AVFrame *frame, int flags); + +/** + * @return a string identifying the side data type + */ +const char *av_frame_side_data_name(enum AVFrameSideDataType type); + +/** + * @} + */ + +#endif /* AVUTIL_FRAME_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hash.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hash.h new file mode 100644 index 0000000..7693e6b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hash.h
@@ -0,0 +1,269 @@ +/* + * Copyright (C) 2013 Reimar Döffinger <Reimar.Doeffinger@gmx.de> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_hash_generic + * Generic hashing API + */ + +#ifndef AVUTIL_HASH_H +#define AVUTIL_HASH_H + +#include <stdint.h> + +#include "version.h" + +/** + * @defgroup lavu_hash Hash Functions + * @ingroup lavu_crypto + * Hash functions useful in multimedia. + * + * Hash functions are widely used in multimedia, from error checking and + * concealment to internal regression testing. libavutil has efficient + * implementations of a variety of hash functions that may be useful for + * FFmpeg and other multimedia applications. + * + * @{ + * + * @defgroup lavu_hash_generic Generic Hashing API + * An abstraction layer for all hash functions supported by libavutil. + * + * If your application needs to support a wide range of different hash + * functions, then the Generic Hashing API is for you. It provides a generic, + * reusable API for @ref lavu_hash "all hash functions" implemented in libavutil. + * If you just need to use one particular hash function, use the @ref lavu_hash + * "individual hash" directly. + * + * @section Sample Code + * + * A basic template for using the Generic Hashing API follows: + * + * @code + * struct AVHashContext *ctx = NULL; + * const char *hash_name = NULL; + * uint8_t *output_buf = NULL; + * + * // Select from a string returned by av_hash_names() + * hash_name = ...; + * + * // Allocate a hash context + * ret = av_hash_alloc(&ctx, hash_name); + * if (ret < 0) + * return ret; + * + * // Initialize the hash context + * av_hash_init(ctx); + * + * // Update the hash context with data + * while (data_left) { + * av_hash_update(ctx, data, size); + * } + * + * // Now we have no more data, so it is time to finalize the hash and get the + * // output. But we need to first allocate an output buffer. Note that you can + * // use any memory allocation function, including malloc(), not just + * // av_malloc(). + * output_buf = av_malloc(av_hash_get_size(ctx)); + * if (!output_buf) + * return AVERROR(ENOMEM); + * + * // Finalize the hash context. + * // You can use any of the av_hash_final*() functions provided, for other + * // output formats. If you do so, be sure to adjust the memory allocation + * // above. See the function documentation below for the exact amount of extra + * // memory needed. + * av_hash_final(ctx, output_buffer); + * + * // Free the context + * av_hash_freep(&ctx); + * @endcode + * + * @section Hash Function-Specific Information + * If the CRC32 hash is selected, the #AV_CRC_32_IEEE polynomial will be + * used. + * + * If the Murmur3 hash is selected, the default seed will be used. See @ref + * lavu_murmur3_seedinfo "Murmur3" for more information. + * + * @{ + */ + +/** + * @example ffhash.c + * This example is a simple command line application that takes one or more + * arguments. It demonstrates a typical use of the hashing API with allocation, + * initialization, updating, and finalizing. + */ + +struct AVHashContext; + +/** + * Allocate a hash context for the algorithm specified by name. + * + * @return >= 0 for success, a negative error code for failure + * + * @note The context is not initialized after a call to this function; you must + * call av_hash_init() to do so. + */ +int av_hash_alloc(struct AVHashContext **ctx, const char *name); + +/** + * Get the names of available hash algorithms. + * + * This function can be used to enumerate the algorithms. + * + * @param[in] i Index of the hash algorithm, starting from 0 + * @return Pointer to a static string or `NULL` if `i` is out of range + */ +const char *av_hash_names(int i); + +/** + * Get the name of the algorithm corresponding to the given hash context. + */ +const char *av_hash_get_name(const struct AVHashContext *ctx); + +/** + * Maximum value that av_hash_get_size() will currently return. + * + * You can use this if you absolutely want or need to use static allocation for + * the output buffer and are fine with not supporting hashes newly added to + * libavutil without recompilation. + * + * @warning + * Adding new hashes with larger sizes, and increasing the macro while doing + * so, will not be considered an ABI change. To prevent your code from + * overflowing a buffer, either dynamically allocate the output buffer with + * av_hash_get_size(), or limit your use of the Hashing API to hashes that are + * already in FFmpeg during the time of compilation. + */ +#define AV_HASH_MAX_SIZE 64 + +/** + * Get the size of the resulting hash value in bytes. + * + * The maximum value this function will currently return is available as macro + * #AV_HASH_MAX_SIZE. + * + * @param[in] ctx Hash context + * @return Size of the hash value in bytes + */ +int av_hash_get_size(const struct AVHashContext *ctx); + +/** + * Initialize or reset a hash context. + * + * @param[in,out] ctx Hash context + */ +void av_hash_init(struct AVHashContext *ctx); + +/** + * Update a hash context with additional data. + * + * @param[in,out] ctx Hash context + * @param[in] src Data to be added to the hash context + * @param[in] len Size of the additional data + */ +#if FF_API_CRYPTO_SIZE_T +void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, int len); +#else +void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, size_t len); +#endif + +/** + * Finalize a hash context and compute the actual hash value. + * + * The minimum size of `dst` buffer is given by av_hash_get_size() or + * #AV_HASH_MAX_SIZE. The use of the latter macro is discouraged. + * + * It is not safe to update or finalize a hash context again, if it has already + * been finalized. + * + * @param[in,out] ctx Hash context + * @param[out] dst Where the final hash value will be stored + * + * @see av_hash_final_bin() provides an alternative API + */ +void av_hash_final(struct AVHashContext *ctx, uint8_t *dst); + +/** + * Finalize a hash context and store the actual hash value in a buffer. + * + * It is not safe to update or finalize a hash context again, if it has already + * been finalized. + * + * If `size` is smaller than the hash size (given by av_hash_get_size()), the + * hash is truncated; if size is larger, the buffer is padded with 0. + * + * @param[in,out] ctx Hash context + * @param[out] dst Where the final hash value will be stored + * @param[in] size Number of bytes to write to `dst` + */ +void av_hash_final_bin(struct AVHashContext *ctx, uint8_t *dst, int size); + +/** + * Finalize a hash context and store the hexadecimal representation of the + * actual hash value as a string. + * + * It is not safe to update or finalize a hash context again, if it has already + * been finalized. + * + * The string is always 0-terminated. + * + * If `size` is smaller than `2 * hash_size + 1`, where `hash_size` is the + * value returned by av_hash_get_size(), the string will be truncated. + * + * @param[in,out] ctx Hash context + * @param[out] dst Where the string will be stored + * @param[in] size Maximum number of bytes to write to `dst` + */ +void av_hash_final_hex(struct AVHashContext *ctx, uint8_t *dst, int size); + +/** + * Finalize a hash context and store the Base64 representation of the + * actual hash value as a string. + * + * It is not safe to update or finalize a hash context again, if it has already + * been finalized. + * + * The string is always 0-terminated. + * + * If `size` is smaller than AV_BASE64_SIZE(hash_size), where `hash_size` is + * the value returned by av_hash_get_size(), the string will be truncated. + * + * @param[in,out] ctx Hash context + * @param[out] dst Where the final hash value will be stored + * @param[in] size Maximum number of bytes to write to `dst` + */ +void av_hash_final_b64(struct AVHashContext *ctx, uint8_t *dst, int size); + +/** + * Free hash context and set hash context pointer to `NULL`. + * + * @param[in,out] ctx Pointer to hash context + */ +void av_hash_freep(struct AVHashContext **ctx); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_HASH_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hmac.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hmac.h new file mode 100644 index 0000000..412e950 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hmac.h
@@ -0,0 +1,100 @@ +/* + * Copyright (C) 2012 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HMAC_H +#define AVUTIL_HMAC_H + +#include <stdint.h> + +#include "version.h" +/** + * @defgroup lavu_hmac HMAC + * @ingroup lavu_crypto + * @{ + */ + +enum AVHMACType { + AV_HMAC_MD5, + AV_HMAC_SHA1, + AV_HMAC_SHA224, + AV_HMAC_SHA256, + AV_HMAC_SHA384, + AV_HMAC_SHA512, +}; + +typedef struct AVHMAC AVHMAC; + +/** + * Allocate an AVHMAC context. + * @param type The hash function used for the HMAC. + */ +AVHMAC *av_hmac_alloc(enum AVHMACType type); + +/** + * Free an AVHMAC context. + * @param ctx The context to free, may be NULL + */ +void av_hmac_free(AVHMAC *ctx); + +/** + * Initialize an AVHMAC context with an authentication key. + * @param ctx The HMAC context + * @param key The authentication key + * @param keylen The length of the key, in bytes + */ +void av_hmac_init(AVHMAC *ctx, const uint8_t *key, unsigned int keylen); + +/** + * Hash data with the HMAC. + * @param ctx The HMAC context + * @param data The data to hash + * @param len The length of the data, in bytes + */ +void av_hmac_update(AVHMAC *ctx, const uint8_t *data, unsigned int len); + +/** + * Finish hashing and output the HMAC digest. + * @param ctx The HMAC context + * @param out The output buffer to write the digest into + * @param outlen The length of the out buffer, in bytes + * @return The number of bytes written to out, or a negative error code. + */ +int av_hmac_final(AVHMAC *ctx, uint8_t *out, unsigned int outlen); + +/** + * Hash an array of data with a key. + * @param ctx The HMAC context + * @param data The data to hash + * @param len The length of the data, in bytes + * @param key The authentication key + * @param keylen The length of the key, in bytes + * @param out The output buffer to write the digest into + * @param outlen The length of the out buffer, in bytes + * @return The number of bytes written to out, or a negative error code. + */ +int av_hmac_calc(AVHMAC *ctx, const uint8_t *data, unsigned int len, + const uint8_t *key, unsigned int keylen, + uint8_t *out, unsigned int outlen); + +/** + * @} + */ + +#endif /* AVUTIL_HMAC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext.h new file mode 100644 index 0000000..f5a4b62 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext.h
@@ -0,0 +1,584 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_H +#define AVUTIL_HWCONTEXT_H + +#include "buffer.h" +#include "frame.h" +#include "log.h" +#include "pixfmt.h" + +enum AVHWDeviceType { + AV_HWDEVICE_TYPE_NONE, + AV_HWDEVICE_TYPE_VDPAU, + AV_HWDEVICE_TYPE_CUDA, + AV_HWDEVICE_TYPE_VAAPI, + AV_HWDEVICE_TYPE_DXVA2, + AV_HWDEVICE_TYPE_QSV, + AV_HWDEVICE_TYPE_VIDEOTOOLBOX, + AV_HWDEVICE_TYPE_D3D11VA, + AV_HWDEVICE_TYPE_DRM, + AV_HWDEVICE_TYPE_OPENCL, + AV_HWDEVICE_TYPE_MEDIACODEC, +}; + +typedef struct AVHWDeviceInternal AVHWDeviceInternal; + +/** + * This struct aggregates all the (hardware/vendor-specific) "high-level" state, + * i.e. state that is not tied to a concrete processing configuration. + * E.g., in an API that supports hardware-accelerated encoding and decoding, + * this struct will (if possible) wrap the state that is common to both encoding + * and decoding and from which specific instances of encoders or decoders can be + * derived. + * + * This struct is reference-counted with the AVBuffer mechanism. The + * av_hwdevice_ctx_alloc() constructor yields a reference, whose data field + * points to the actual AVHWDeviceContext. Further objects derived from + * AVHWDeviceContext (such as AVHWFramesContext, describing a frame pool with + * specific properties) will hold an internal reference to it. After all the + * references are released, the AVHWDeviceContext itself will be freed, + * optionally invoking a user-specified callback for uninitializing the hardware + * state. + */ +typedef struct AVHWDeviceContext { + /** + * A class for logging. Set by av_hwdevice_ctx_alloc(). + */ + const AVClass *av_class; + + /** + * Private data used internally by libavutil. Must not be accessed in any + * way by the caller. + */ + AVHWDeviceInternal *internal; + + /** + * This field identifies the underlying API used for hardware access. + * + * This field is set when this struct is allocated and never changed + * afterwards. + */ + enum AVHWDeviceType type; + + /** + * The format-specific data, allocated and freed by libavutil along with + * this context. + * + * Should be cast by the user to the format-specific context defined in the + * corresponding header (hwcontext_*.h) and filled as described in the + * documentation before calling av_hwdevice_ctx_init(). + * + * After calling av_hwdevice_ctx_init() this struct should not be modified + * by the caller. + */ + void *hwctx; + + /** + * This field may be set by the caller before calling av_hwdevice_ctx_init(). + * + * If non-NULL, this callback will be called when the last reference to + * this context is unreferenced, immediately before it is freed. + * + * @note when other objects (e.g an AVHWFramesContext) are derived from this + * struct, this callback will be invoked after all such child objects + * are fully uninitialized and their respective destructors invoked. + */ + void (*free)(struct AVHWDeviceContext *ctx); + + /** + * Arbitrary user data, to be used e.g. by the free() callback. + */ + void *user_opaque; +} AVHWDeviceContext; + +typedef struct AVHWFramesInternal AVHWFramesInternal; + +/** + * This struct describes a set or pool of "hardware" frames (i.e. those with + * data not located in normal system memory). All the frames in the pool are + * assumed to be allocated in the same way and interchangeable. + * + * This struct is reference-counted with the AVBuffer mechanism and tied to a + * given AVHWDeviceContext instance. The av_hwframe_ctx_alloc() constructor + * yields a reference, whose data field points to the actual AVHWFramesContext + * struct. + */ +typedef struct AVHWFramesContext { + /** + * A class for logging. + */ + const AVClass *av_class; + + /** + * Private data used internally by libavutil. Must not be accessed in any + * way by the caller. + */ + AVHWFramesInternal *internal; + + /** + * A reference to the parent AVHWDeviceContext. This reference is owned and + * managed by the enclosing AVHWFramesContext, but the caller may derive + * additional references from it. + */ + AVBufferRef *device_ref; + + /** + * The parent AVHWDeviceContext. This is simply a pointer to + * device_ref->data provided for convenience. + * + * Set by libavutil in av_hwframe_ctx_init(). + */ + AVHWDeviceContext *device_ctx; + + /** + * The format-specific data, allocated and freed automatically along with + * this context. + * + * Should be cast by the user to the format-specific context defined in the + * corresponding header (hwframe_*.h) and filled as described in the + * documentation before calling av_hwframe_ctx_init(). + * + * After any frames using this context are created, the contents of this + * struct should not be modified by the caller. + */ + void *hwctx; + + /** + * This field may be set by the caller before calling av_hwframe_ctx_init(). + * + * If non-NULL, this callback will be called when the last reference to + * this context is unreferenced, immediately before it is freed. + */ + void (*free)(struct AVHWFramesContext *ctx); + + /** + * Arbitrary user data, to be used e.g. by the free() callback. + */ + void *user_opaque; + + /** + * A pool from which the frames are allocated by av_hwframe_get_buffer(). + * This field may be set by the caller before calling av_hwframe_ctx_init(). + * The buffers returned by calling av_buffer_pool_get() on this pool must + * have the properties described in the documentation in the corresponding hw + * type's header (hwcontext_*.h). The pool will be freed strictly before + * this struct's free() callback is invoked. + * + * This field may be NULL, then libavutil will attempt to allocate a pool + * internally. Note that certain device types enforce pools allocated at + * fixed size (frame count), which cannot be extended dynamically. In such a + * case, initial_pool_size must be set appropriately. + */ + AVBufferPool *pool; + + /** + * Initial size of the frame pool. If a device type does not support + * dynamically resizing the pool, then this is also the maximum pool size. + * + * May be set by the caller before calling av_hwframe_ctx_init(). Must be + * set if pool is NULL and the device type does not support dynamic pools. + */ + int initial_pool_size; + + /** + * The pixel format identifying the underlying HW surface type. + * + * Must be a hwaccel format, i.e. the corresponding descriptor must have the + * AV_PIX_FMT_FLAG_HWACCEL flag set. + * + * Must be set by the user before calling av_hwframe_ctx_init(). + */ + enum AVPixelFormat format; + + /** + * The pixel format identifying the actual data layout of the hardware + * frames. + * + * Must be set by the caller before calling av_hwframe_ctx_init(). + * + * @note when the underlying API does not provide the exact data layout, but + * only the colorspace/bit depth, this field should be set to the fully + * planar version of that format (e.g. for 8-bit 420 YUV it should be + * AV_PIX_FMT_YUV420P, not AV_PIX_FMT_NV12 or anything else). + */ + enum AVPixelFormat sw_format; + + /** + * The allocated dimensions of the frames in this pool. + * + * Must be set by the user before calling av_hwframe_ctx_init(). + */ + int width, height; +} AVHWFramesContext; + +/** + * Look up an AVHWDeviceType by name. + * + * @param name String name of the device type (case-insensitive). + * @return The type from enum AVHWDeviceType, or AV_HWDEVICE_TYPE_NONE if + * not found. + */ +enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name); + +/** Get the string name of an AVHWDeviceType. + * + * @param type Type from enum AVHWDeviceType. + * @return Pointer to a static string containing the name, or NULL if the type + * is not valid. + */ +const char *av_hwdevice_get_type_name(enum AVHWDeviceType type); + +/** + * Iterate over supported device types. + * + * @param type AV_HWDEVICE_TYPE_NONE initially, then the previous type + * returned by this function in subsequent iterations. + * @return The next usable device type from enum AVHWDeviceType, or + * AV_HWDEVICE_TYPE_NONE if there are no more. + */ +enum AVHWDeviceType av_hwdevice_iterate_types(enum AVHWDeviceType prev); + +/** + * Allocate an AVHWDeviceContext for a given hardware type. + * + * @param type the type of the hardware device to allocate. + * @return a reference to the newly created AVHWDeviceContext on success or NULL + * on failure. + */ +AVBufferRef *av_hwdevice_ctx_alloc(enum AVHWDeviceType type); + +/** + * Finalize the device context before use. This function must be called after + * the context is filled with all the required information and before it is + * used in any way. + * + * @param ref a reference to the AVHWDeviceContext + * @return 0 on success, a negative AVERROR code on failure + */ +int av_hwdevice_ctx_init(AVBufferRef *ref); + +/** + * Open a device of the specified type and create an AVHWDeviceContext for it. + * + * This is a convenience function intended to cover the simple cases. Callers + * who need to fine-tune device creation/management should open the device + * manually and then wrap it in an AVHWDeviceContext using + * av_hwdevice_ctx_alloc()/av_hwdevice_ctx_init(). + * + * The returned context is already initialized and ready for use, the caller + * should not call av_hwdevice_ctx_init() on it. The user_opaque/free fields of + * the created AVHWDeviceContext are set by this function and should not be + * touched by the caller. + * + * @param device_ctx On success, a reference to the newly-created device context + * will be written here. The reference is owned by the caller + * and must be released with av_buffer_unref() when no longer + * needed. On failure, NULL will be written to this pointer. + * @param type The type of the device to create. + * @param device A type-specific string identifying the device to open. + * @param opts A dictionary of additional (type-specific) options to use in + * opening the device. The dictionary remains owned by the caller. + * @param flags currently unused + * + * @return 0 on success, a negative AVERROR code on failure. + */ +int av_hwdevice_ctx_create(AVBufferRef **device_ctx, enum AVHWDeviceType type, + const char *device, AVDictionary *opts, int flags); + +/** + * Create a new device of the specified type from an existing device. + * + * If the source device is a device of the target type or was originally + * derived from such a device (possibly through one or more intermediate + * devices of other types), then this will return a reference to the + * existing device of the same type as is requested. + * + * Otherwise, it will attempt to derive a new device from the given source + * device. If direct derivation to the new type is not implemented, it will + * attempt the same derivation from each ancestor of the source device in + * turn looking for an implemented derivation method. + * + * @param dst_ctx On success, a reference to the newly-created + * AVHWDeviceContext. + * @param type The type of the new device to create. + * @param src_ctx A reference to an existing AVHWDeviceContext which will be + * used to create the new device. + * @param flags Currently unused; should be set to zero. + * @return Zero on success, a negative AVERROR code on failure. + */ +int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ctx, + enum AVHWDeviceType type, + AVBufferRef *src_ctx, int flags); + + +/** + * Allocate an AVHWFramesContext tied to a given device context. + * + * @param device_ctx a reference to a AVHWDeviceContext. This function will make + * a new reference for internal use, the one passed to the + * function remains owned by the caller. + * @return a reference to the newly created AVHWFramesContext on success or NULL + * on failure. + */ +AVBufferRef *av_hwframe_ctx_alloc(AVBufferRef *device_ctx); + +/** + * Finalize the context before use. This function must be called after the + * context is filled with all the required information and before it is attached + * to any frames. + * + * @param ref a reference to the AVHWFramesContext + * @return 0 on success, a negative AVERROR code on failure + */ +int av_hwframe_ctx_init(AVBufferRef *ref); + +/** + * Allocate a new frame attached to the given AVHWFramesContext. + * + * @param hwframe_ctx a reference to an AVHWFramesContext + * @param frame an empty (freshly allocated or unreffed) frame to be filled with + * newly allocated buffers. + * @param flags currently unused, should be set to zero + * @return 0 on success, a negative AVERROR code on failure + */ +int av_hwframe_get_buffer(AVBufferRef *hwframe_ctx, AVFrame *frame, int flags); + +/** + * Copy data to or from a hw surface. At least one of dst/src must have an + * AVHWFramesContext attached. + * + * If src has an AVHWFramesContext attached, then the format of dst (if set) + * must use one of the formats returned by av_hwframe_transfer_get_formats(src, + * AV_HWFRAME_TRANSFER_DIRECTION_FROM). + * If dst has an AVHWFramesContext attached, then the format of src must use one + * of the formats returned by av_hwframe_transfer_get_formats(dst, + * AV_HWFRAME_TRANSFER_DIRECTION_TO) + * + * dst may be "clean" (i.e. with data/buf pointers unset), in which case the + * data buffers will be allocated by this function using av_frame_get_buffer(). + * If dst->format is set, then this format will be used, otherwise (when + * dst->format is AV_PIX_FMT_NONE) the first acceptable format will be chosen. + * + * The two frames must have matching allocated dimensions (i.e. equal to + * AVHWFramesContext.width/height), since not all device types support + * transferring a sub-rectangle of the whole surface. The display dimensions + * (i.e. AVFrame.width/height) may be smaller than the allocated dimensions, but + * also have to be equal for both frames. When the display dimensions are + * smaller than the allocated dimensions, the content of the padding in the + * destination frame is unspecified. + * + * @param dst the destination frame. dst is not touched on failure. + * @param src the source frame. + * @param flags currently unused, should be set to zero + * @return 0 on success, a negative AVERROR error code on failure. + */ +int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags); + +enum AVHWFrameTransferDirection { + /** + * Transfer the data from the queried hw frame. + */ + AV_HWFRAME_TRANSFER_DIRECTION_FROM, + + /** + * Transfer the data to the queried hw frame. + */ + AV_HWFRAME_TRANSFER_DIRECTION_TO, +}; + +/** + * Get a list of possible source or target formats usable in + * av_hwframe_transfer_data(). + * + * @param hwframe_ctx the frame context to obtain the information for + * @param dir the direction of the transfer + * @param formats the pointer to the output format list will be written here. + * The list is terminated with AV_PIX_FMT_NONE and must be freed + * by the caller when no longer needed using av_free(). + * If this function returns successfully, the format list will + * have at least one item (not counting the terminator). + * On failure, the contents of this pointer are unspecified. + * @param flags currently unused, should be set to zero + * @return 0 on success, a negative AVERROR code on failure. + */ +int av_hwframe_transfer_get_formats(AVBufferRef *hwframe_ctx, + enum AVHWFrameTransferDirection dir, + enum AVPixelFormat **formats, int flags); + + +/** + * This struct describes the constraints on hardware frames attached to + * a given device with a hardware-specific configuration. This is returned + * by av_hwdevice_get_hwframe_constraints() and must be freed by + * av_hwframe_constraints_free() after use. + */ +typedef struct AVHWFramesConstraints { + /** + * A list of possible values for format in the hw_frames_ctx, + * terminated by AV_PIX_FMT_NONE. This member will always be filled. + */ + enum AVPixelFormat *valid_hw_formats; + + /** + * A list of possible values for sw_format in the hw_frames_ctx, + * terminated by AV_PIX_FMT_NONE. Can be NULL if this information is + * not known. + */ + enum AVPixelFormat *valid_sw_formats; + + /** + * The minimum size of frames in this hw_frames_ctx. + * (Zero if not known.) + */ + int min_width; + int min_height; + + /** + * The maximum size of frames in this hw_frames_ctx. + * (INT_MAX if not known / no limit.) + */ + int max_width; + int max_height; +} AVHWFramesConstraints; + +/** + * Allocate a HW-specific configuration structure for a given HW device. + * After use, the user must free all members as required by the specific + * hardware structure being used, then free the structure itself with + * av_free(). + * + * @param device_ctx a reference to the associated AVHWDeviceContext. + * @return The newly created HW-specific configuration structure on + * success or NULL on failure. + */ +void *av_hwdevice_hwconfig_alloc(AVBufferRef *device_ctx); + +/** + * Get the constraints on HW frames given a device and the HW-specific + * configuration to be used with that device. If no HW-specific + * configuration is provided, returns the maximum possible capabilities + * of the device. + * + * @param ref a reference to the associated AVHWDeviceContext. + * @param hwconfig a filled HW-specific configuration structure, or NULL + * to return the maximum possible capabilities of the device. + * @return AVHWFramesConstraints structure describing the constraints + * on the device, or NULL if not available. + */ +AVHWFramesConstraints *av_hwdevice_get_hwframe_constraints(AVBufferRef *ref, + const void *hwconfig); + +/** + * Free an AVHWFrameConstraints structure. + * + * @param constraints The (filled or unfilled) AVHWFrameConstraints structure. + */ +void av_hwframe_constraints_free(AVHWFramesConstraints **constraints); + + +/** + * Flags to apply to frame mappings. + */ +enum { + /** + * The mapping must be readable. + */ + AV_HWFRAME_MAP_READ = 1 << 0, + /** + * The mapping must be writeable. + */ + AV_HWFRAME_MAP_WRITE = 1 << 1, + /** + * The mapped frame will be overwritten completely in subsequent + * operations, so the current frame data need not be loaded. Any values + * which are not overwritten are unspecified. + */ + AV_HWFRAME_MAP_OVERWRITE = 1 << 2, + /** + * The mapping must be direct. That is, there must not be any copying in + * the map or unmap steps. Note that performance of direct mappings may + * be much lower than normal memory. + */ + AV_HWFRAME_MAP_DIRECT = 1 << 3, +}; + +/** + * Map a hardware frame. + * + * This has a number of different possible effects, depending on the format + * and origin of the src and dst frames. On input, src should be a usable + * frame with valid buffers and dst should be blank (typically as just created + * by av_frame_alloc()). src should have an associated hwframe context, and + * dst may optionally have a format and associated hwframe context. + * + * If src was created by mapping a frame from the hwframe context of dst, + * then this function undoes the mapping - dst is replaced by a reference to + * the frame that src was originally mapped from. + * + * If both src and dst have an associated hwframe context, then this function + * attempts to map the src frame from its hardware context to that of dst and + * then fill dst with appropriate data to be usable there. This will only be + * possible if the hwframe contexts and associated devices are compatible - + * given compatible devices, av_hwframe_ctx_create_derived() can be used to + * create a hwframe context for dst in which mapping should be possible. + * + * If src has a hwframe context but dst does not, then the src frame is + * mapped to normal memory and should thereafter be usable as a normal frame. + * If the format is set on dst, then the mapping will attempt to create dst + * with that format and fail if it is not possible. If format is unset (is + * AV_PIX_FMT_NONE) then dst will be mapped with whatever the most appropriate + * format to use is (probably the sw_format of the src hwframe context). + * + * A return value of AVERROR(ENOSYS) indicates that the mapping is not + * possible with the given arguments and hwframe setup, while other return + * values indicate that it failed somehow. + * + * @param dst Destination frame, to contain the mapping. + * @param src Source frame, to be mapped. + * @param flags Some combination of AV_HWFRAME_MAP_* flags. + * @return Zero on success, negative AVERROR code on failure. + */ +int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags); + + +/** + * Create and initialise an AVHWFramesContext as a mapping of another existing + * AVHWFramesContext on a different device. + * + * av_hwframe_ctx_init() should not be called after this. + * + * @param derived_frame_ctx On success, a reference to the newly created + * AVHWFramesContext. + * @param derived_device_ctx A reference to the device to create the new + * AVHWFramesContext on. + * @param source_frame_ctx A reference to an existing AVHWFramesContext + * which will be mapped to the derived context. + * @param flags Some combination of AV_HWFRAME_MAP_* flags, defining the + * mapping parameters to apply to frames which are allocated + * in the derived device. + * @return Zero on success, negative AVERROR code on failure. + */ +int av_hwframe_ctx_create_derived(AVBufferRef **derived_frame_ctx, + enum AVPixelFormat format, + AVBufferRef *derived_device_ctx, + AVBufferRef *source_frame_ctx, + int flags); + +#endif /* AVUTIL_HWCONTEXT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_cuda.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_cuda.h new file mode 100644 index 0000000..81a0552 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_cuda.h
@@ -0,0 +1,52 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef AVUTIL_HWCONTEXT_CUDA_H +#define AVUTIL_HWCONTEXT_CUDA_H + +#ifndef CUDA_VERSION +#include <cuda.h> +#endif + +#include "pixfmt.h" + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_CUDA. + * + * This API supports dynamic frame pools. AVHWFramesContext.pool must return + * AVBufferRefs whose data pointer is a CUdeviceptr. + */ + +typedef struct AVCUDADeviceContextInternal AVCUDADeviceContextInternal; + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVCUDADeviceContext { + CUcontext cuda_ctx; + CUstream stream; + AVCUDADeviceContextInternal *internal; +} AVCUDADeviceContext; + +/** + * AVHWFramesContext.hwctx is currently not used + */ + +#endif /* AVUTIL_HWCONTEXT_CUDA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_d3d11va.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_d3d11va.h new file mode 100644 index 0000000..9f91e9b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_d3d11va.h
@@ -0,0 +1,169 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_D3D11VA_H +#define AVUTIL_HWCONTEXT_D3D11VA_H + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_D3D11VA. + * + * The default pool implementation will be fixed-size if initial_pool_size is + * set (and allocate elements from an array texture). Otherwise it will allocate + * individual textures. Be aware that decoding requires a single array texture. + * + * Using sw_format==AV_PIX_FMT_YUV420P has special semantics, and maps to + * DXGI_FORMAT_420_OPAQUE. av_hwframe_transfer_data() is not supported for + * this format. Refer to MSDN for details. + * + * av_hwdevice_ctx_create() for this device type supports a key named "debug" + * for the AVDictionary entry. If this is set to any value, the device creation + * code will try to load various supported D3D debugging layers. + */ + +#include <d3d11.h> +#include <stdint.h> + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVD3D11VADeviceContext { + /** + * Device used for texture creation and access. This can also be used to + * set the libavcodec decoding device. + * + * Must be set by the user. This is the only mandatory field - the other + * device context fields are set from this and are available for convenience. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11Device *device; + + /** + * If unset, this will be set from the device field on init. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11DeviceContext *device_context; + + /** + * If unset, this will be set from the device field on init. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11VideoDevice *video_device; + + /** + * If unset, this will be set from the device_context field on init. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11VideoContext *video_context; + + /** + * Callbacks for locking. They protect accesses to device_context and + * video_context calls. They also protect access to the internal staging + * texture (for av_hwframe_transfer_data() calls). They do NOT protect + * access to hwcontext or decoder state in general. + * + * If unset on init, the hwcontext implementation will set them to use an + * internal mutex. + * + * The underlying lock must be recursive. lock_ctx is for free use by the + * locking implementation. + */ + void (*lock)(void *lock_ctx); + void (*unlock)(void *lock_ctx); + void *lock_ctx; +} AVD3D11VADeviceContext; + +/** + * D3D11 frame descriptor for pool allocation. + * + * In user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer pointing at an object of this type describing the + * planes of the frame. + * + * This has no use outside of custom allocation, and AVFrame AVBufferRef do not + * necessarily point to an instance of this struct. + */ +typedef struct AVD3D11FrameDescriptor { + /** + * The texture in which the frame is located. The reference count is + * managed by the AVBufferRef, and destroying the reference will release + * the interface. + * + * Normally stored in AVFrame.data[0]. + */ + ID3D11Texture2D *texture; + + /** + * The index into the array texture element representing the frame, or 0 + * if the texture is not an array texture. + * + * Normally stored in AVFrame.data[1] (cast from intptr_t). + */ + intptr_t index; +} AVD3D11FrameDescriptor; + +/** + * This struct is allocated as AVHWFramesContext.hwctx + */ +typedef struct AVD3D11VAFramesContext { + /** + * The canonical texture used for pool allocation. If this is set to NULL + * on init, the hwframes implementation will allocate and set an array + * texture if initial_pool_size > 0. + * + * The only situation when the API user should set this is: + * - the user wants to do manual pool allocation (setting + * AVHWFramesContext.pool), instead of letting AVHWFramesContext + * allocate the pool + * - of an array texture + * - and wants it to use it for decoding + * - this has to be done before calling av_hwframe_ctx_init() + * + * Deallocating the AVHWFramesContext will always release this interface, + * and it does not matter whether it was user-allocated. + * + * This is in particular used by the libavcodec D3D11VA hwaccel, which + * requires a single array texture. It will create ID3D11VideoDecoderOutputView + * objects for each array texture element on decoder initialization. + */ + ID3D11Texture2D *texture; + + /** + * D3D11_TEXTURE2D_DESC.BindFlags used for texture creation. The user must + * at least set D3D11_BIND_DECODER if the frames context is to be used for + * video decoding. + * This field is ignored/invalid if a user-allocated texture is provided. + */ + UINT BindFlags; + + /** + * D3D11_TEXTURE2D_DESC.MiscFlags used for texture creation. + * This field is ignored/invalid if a user-allocated texture is provided. + */ + UINT MiscFlags; +} AVD3D11VAFramesContext; + +#endif /* AVUTIL_HWCONTEXT_D3D11VA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_drm.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_drm.h new file mode 100644 index 0000000..42709f2 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_drm.h
@@ -0,0 +1,169 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_DRM_H +#define AVUTIL_HWCONTEXT_DRM_H + +#include <stddef.h> +#include <stdint.h> + +/** + * @file + * API-specific header for AV_HWDEVICE_TYPE_DRM. + * + * Internal frame allocation is not currently supported - all frames + * must be allocated by the user. Thus AVHWFramesContext is always + * NULL, though this may change if support for frame allocation is + * added in future. + */ + +enum { + /** + * The maximum number of layers/planes in a DRM frame. + */ + AV_DRM_MAX_PLANES = 4 +}; + +/** + * DRM object descriptor. + * + * Describes a single DRM object, addressing it as a PRIME file + * descriptor. + */ +typedef struct AVDRMObjectDescriptor { + /** + * DRM PRIME fd for the object. + */ + int fd; + /** + * Total size of the object. + * + * (This includes any parts not which do not contain image data.) + */ + size_t size; + /** + * Format modifier applied to the object (DRM_FORMAT_MOD_*). + * + * If the format modifier is unknown then this should be set to + * DRM_FORMAT_MOD_INVALID. + */ + uint64_t format_modifier; +} AVDRMObjectDescriptor; + +/** + * DRM plane descriptor. + * + * Describes a single plane of a layer, which is contained within + * a single object. + */ +typedef struct AVDRMPlaneDescriptor { + /** + * Index of the object containing this plane in the objects + * array of the enclosing frame descriptor. + */ + int object_index; + /** + * Offset within that object of this plane. + */ + ptrdiff_t offset; + /** + * Pitch (linesize) of this plane. + */ + ptrdiff_t pitch; +} AVDRMPlaneDescriptor; + +/** + * DRM layer descriptor. + * + * Describes a single layer within a frame. This has the structure + * defined by its format, and will contain one or more planes. + */ +typedef struct AVDRMLayerDescriptor { + /** + * Format of the layer (DRM_FORMAT_*). + */ + uint32_t format; + /** + * Number of planes in the layer. + * + * This must match the number of planes required by format. + */ + int nb_planes; + /** + * Array of planes in this layer. + */ + AVDRMPlaneDescriptor planes[AV_DRM_MAX_PLANES]; +} AVDRMLayerDescriptor; + +/** + * DRM frame descriptor. + * + * This is used as the data pointer for AV_PIX_FMT_DRM_PRIME frames. + * It is also used by user-allocated frame pools - allocating in + * AVHWFramesContext.pool must return AVBufferRefs which contain + * an object of this type. + * + * The fields of this structure should be set such it can be + * imported directly by EGL using the EGL_EXT_image_dma_buf_import + * and EGL_EXT_image_dma_buf_import_modifiers extensions. + * (Note that the exact layout of a particular format may vary between + * platforms - we only specify that the same platform should be able + * to import it.) + * + * The total number of planes must not exceed AV_DRM_MAX_PLANES, and + * the order of the planes by increasing layer index followed by + * increasing plane index must be the same as the order which would + * be used for the data pointers in the equivalent software format. + */ +typedef struct AVDRMFrameDescriptor { + /** + * Number of DRM objects making up this frame. + */ + int nb_objects; + /** + * Array of objects making up the frame. + */ + AVDRMObjectDescriptor objects[AV_DRM_MAX_PLANES]; + /** + * Number of layers in the frame. + */ + int nb_layers; + /** + * Array of layers in the frame. + */ + AVDRMLayerDescriptor layers[AV_DRM_MAX_PLANES]; +} AVDRMFrameDescriptor; + +/** + * DRM device. + * + * Allocated as AVHWDeviceContext.hwctx. + */ +typedef struct AVDRMDeviceContext { + /** + * File descriptor of DRM device. + * + * This is used as the device to create frames on, and may also be + * used in some derivation and mapping operations. + * + * If no device is required, set to -1. + */ + int fd; +} AVDRMDeviceContext; + +#endif /* AVUTIL_HWCONTEXT_DRM_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_dxva2.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_dxva2.h new file mode 100644 index 0000000..e1b79bc --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_dxva2.h
@@ -0,0 +1,75 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef AVUTIL_HWCONTEXT_DXVA2_H +#define AVUTIL_HWCONTEXT_DXVA2_H + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_DXVA2. + * + * Only fixed-size pools are supported. + * + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer set to a pointer to IDirect3DSurface9. + */ + +#include <d3d9.h> +#include <dxva2api.h> + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVDXVA2DeviceContext { + IDirect3DDeviceManager9 *devmgr; +} AVDXVA2DeviceContext; + +/** + * This struct is allocated as AVHWFramesContext.hwctx + */ +typedef struct AVDXVA2FramesContext { + /** + * The surface type (e.g. DXVA2_VideoProcessorRenderTarget or + * DXVA2_VideoDecoderRenderTarget). Must be set by the caller. + */ + DWORD surface_type; + + /** + * The surface pool. When an external pool is not provided by the caller, + * this will be managed (allocated and filled on init, freed on uninit) by + * libavutil. + */ + IDirect3DSurface9 **surfaces; + int nb_surfaces; + + /** + * Certain drivers require the decoder to be destroyed before the surfaces. + * To allow internally managed pools to work properly in such cases, this + * field is provided. + * + * If it is non-NULL, libavutil will call IDirectXVideoDecoder_Release() on + * it just before the internal surface pool is freed. + * + * This is for convenience only. Some code uses other methods to manage the + * decoder reference. + */ + IDirectXVideoDecoder *decoder_to_release; +} AVDXVA2FramesContext; + +#endif /* AVUTIL_HWCONTEXT_DXVA2_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_mediacodec.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_mediacodec.h new file mode 100644 index 0000000..101a980 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_mediacodec.h
@@ -0,0 +1,36 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_MEDIACODEC_H +#define AVUTIL_HWCONTEXT_MEDIACODEC_H + +/** + * MediaCodec details. + * + * Allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVMediaCodecDeviceContext { + /** + * android/view/Surface handle, to be filled by the user. + * + * This is the default surface used by decoders on this device. + */ + void *surface; +} AVMediaCodecDeviceContext; + +#endif /* AVUTIL_HWCONTEXT_MEDIACODEC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_qsv.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_qsv.h new file mode 100644 index 0000000..b98d611 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_qsv.h
@@ -0,0 +1,53 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_QSV_H +#define AVUTIL_HWCONTEXT_QSV_H + +#include <mfx/mfxvideo.h> + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_QSV. + * + * This API does not support dynamic frame pools. AVHWFramesContext.pool must + * contain AVBufferRefs whose data pointer points to an mfxFrameSurface1 struct. + */ + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVQSVDeviceContext { + mfxSession session; +} AVQSVDeviceContext; + +/** + * This struct is allocated as AVHWFramesContext.hwctx + */ +typedef struct AVQSVFramesContext { + mfxFrameSurface1 *surfaces; + int nb_surfaces; + + /** + * A combination of MFX_MEMTYPE_* describing the frame pool. + */ + int frame_type; +} AVQSVFramesContext; + +#endif /* AVUTIL_HWCONTEXT_QSV_H */ +
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vaapi.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vaapi.h new file mode 100644 index 0000000..0b2e071 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vaapi.h
@@ -0,0 +1,117 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_VAAPI_H +#define AVUTIL_HWCONTEXT_VAAPI_H + +#include <va/va.h> + +/** + * @file + * API-specific header for AV_HWDEVICE_TYPE_VAAPI. + * + * Dynamic frame pools are supported, but note that any pool used as a render + * target is required to be of fixed size in order to be be usable as an + * argument to vaCreateContext(). + * + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer set to a VASurfaceID. + */ + +enum { + /** + * The quirks field has been set by the user and should not be detected + * automatically by av_hwdevice_ctx_init(). + */ + AV_VAAPI_DRIVER_QUIRK_USER_SET = (1 << 0), + /** + * The driver does not destroy parameter buffers when they are used by + * vaRenderPicture(). Additional code will be required to destroy them + * separately afterwards. + */ + AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS = (1 << 1), + + /** + * The driver does not support the VASurfaceAttribMemoryType attribute, + * so the surface allocation code will not try to use it. + */ + AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE = (1 << 2), + + /** + * The driver does not support surface attributes at all. + * The surface allocation code will never pass them to surface allocation, + * and the results of the vaQuerySurfaceAttributes() call will be faked. + */ + AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES = (1 << 3), +}; + +/** + * VAAPI connection details. + * + * Allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVVAAPIDeviceContext { + /** + * The VADisplay handle, to be filled by the user. + */ + VADisplay display; + /** + * Driver quirks to apply - this is filled by av_hwdevice_ctx_init(), + * with reference to a table of known drivers, unless the + * AV_VAAPI_DRIVER_QUIRK_USER_SET bit is already present. The user + * may need to refer to this field when performing any later + * operations using VAAPI with the same VADisplay. + */ + unsigned int driver_quirks; +} AVVAAPIDeviceContext; + +/** + * VAAPI-specific data associated with a frame pool. + * + * Allocated as AVHWFramesContext.hwctx. + */ +typedef struct AVVAAPIFramesContext { + /** + * Set by the user to apply surface attributes to all surfaces in + * the frame pool. If null, default settings are used. + */ + VASurfaceAttrib *attributes; + int nb_attributes; + /** + * The surfaces IDs of all surfaces in the pool after creation. + * Only valid if AVHWFramesContext.initial_pool_size was positive. + * These are intended to be used as the render_targets arguments to + * vaCreateContext(). + */ + VASurfaceID *surface_ids; + int nb_surfaces; +} AVVAAPIFramesContext; + +/** + * VAAPI hardware pipeline configuration details. + * + * Allocated with av_hwdevice_hwconfig_alloc(). + */ +typedef struct AVVAAPIHWConfig { + /** + * ID of a VAAPI pipeline configuration. + */ + VAConfigID config_id; +} AVVAAPIHWConfig; + +#endif /* AVUTIL_HWCONTEXT_VAAPI_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vdpau.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vdpau.h new file mode 100644 index 0000000..1b7ea1e --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vdpau.h
@@ -0,0 +1,44 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_VDPAU_H +#define AVUTIL_HWCONTEXT_VDPAU_H + +#include <vdpau/vdpau.h> + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_VDPAU. + * + * This API supports dynamic frame pools. AVHWFramesContext.pool must return + * AVBufferRefs whose data pointer is a VdpVideoSurface. + */ + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVVDPAUDeviceContext { + VdpDevice device; + VdpGetProcAddress *get_proc_address; +} AVVDPAUDeviceContext; + +/** + * AVHWFramesContext.hwctx is currently not used + */ + +#endif /* AVUTIL_HWCONTEXT_VDPAU_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_videotoolbox.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_videotoolbox.h new file mode 100644 index 0000000..380918d --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_videotoolbox.h
@@ -0,0 +1,54 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H +#define AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H + +#include <stdint.h> + +#include <VideoToolbox/VideoToolbox.h> + +#include "pixfmt.h" + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_VIDEOTOOLBOX. + * + * This API currently does not support frame allocation, as the raw VideoToolbox + * API does allocation, and FFmpeg itself never has the need to allocate frames. + * + * If the API user sets a custom pool, AVHWFramesContext.pool must return + * AVBufferRefs whose data pointer is a CVImageBufferRef or CVPixelBufferRef. + * + * Currently AVHWDeviceContext.hwctx and AVHWFramesContext.hwctx are always + * NULL. + */ + +/** + * Convert a VideoToolbox (actually CoreVideo) format to AVPixelFormat. + * Returns AV_PIX_FMT_NONE if no known equivalent was found. + */ +enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt); + +/** + * Convert an AVPixelFormat to a VideoToolbox (actually CoreVideo) format. + * Returns 0 if no known equivalent was found. + */ +uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt); + +#endif /* AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/imgutils.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/imgutils.h new file mode 100644 index 0000000..5b790ec --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/imgutils.h
@@ -0,0 +1,277 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_IMGUTILS_H +#define AVUTIL_IMGUTILS_H + +/** + * @file + * misc image utilities + * + * @addtogroup lavu_picture + * @{ + */ + +#include "avutil.h" +#include "pixdesc.h" +#include "rational.h" + +/** + * Compute the max pixel step for each plane of an image with a + * format described by pixdesc. + * + * The pixel step is the distance in bytes between the first byte of + * the group of bytes which describe a pixel component and the first + * byte of the successive group in the same plane for the same + * component. + * + * @param max_pixsteps an array which is filled with the max pixel step + * for each plane. Since a plane may contain different pixel + * components, the computed max_pixsteps[plane] is relative to the + * component in the plane with the max pixel step. + * @param max_pixstep_comps an array which is filled with the component + * for each plane which has the max pixel step. May be NULL. + */ +void av_image_fill_max_pixsteps(int max_pixsteps[4], int max_pixstep_comps[4], + const AVPixFmtDescriptor *pixdesc); + +/** + * Compute the size of an image line with format pix_fmt and width + * width for the plane plane. + * + * @return the computed size in bytes + */ +int av_image_get_linesize(enum AVPixelFormat pix_fmt, int width, int plane); + +/** + * Fill plane linesizes for an image with pixel format pix_fmt and + * width width. + * + * @param linesizes array to be filled with the linesize for each plane + * @return >= 0 in case of success, a negative error code otherwise + */ +int av_image_fill_linesizes(int linesizes[4], enum AVPixelFormat pix_fmt, int width); + +/** + * Fill plane data pointers for an image with pixel format pix_fmt and + * height height. + * + * @param data pointers array to be filled with the pointer for each image plane + * @param ptr the pointer to a buffer which will contain the image + * @param linesizes the array containing the linesize for each + * plane, should be filled by av_image_fill_linesizes() + * @return the size in bytes required for the image buffer, a negative + * error code in case of failure + */ +int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int height, + uint8_t *ptr, const int linesizes[4]); + +/** + * Allocate an image with size w and h and pixel format pix_fmt, and + * fill pointers and linesizes accordingly. + * The allocated image buffer has to be freed by using + * av_freep(&pointers[0]). + * + * @param align the value to use for buffer size alignment + * @return the size in bytes required for the image buffer, a negative + * error code in case of failure + */ +int av_image_alloc(uint8_t *pointers[4], int linesizes[4], + int w, int h, enum AVPixelFormat pix_fmt, int align); + +/** + * Copy image plane from src to dst. + * That is, copy "height" number of lines of "bytewidth" bytes each. + * The first byte of each successive line is separated by *_linesize + * bytes. + * + * bytewidth must be contained by both absolute values of dst_linesize + * and src_linesize, otherwise the function behavior is undefined. + * + * @param dst_linesize linesize for the image plane in dst + * @param src_linesize linesize for the image plane in src + */ +void av_image_copy_plane(uint8_t *dst, int dst_linesize, + const uint8_t *src, int src_linesize, + int bytewidth, int height); + +/** + * Copy image in src_data to dst_data. + * + * @param dst_linesizes linesizes for the image in dst_data + * @param src_linesizes linesizes for the image in src_data + */ +void av_image_copy(uint8_t *dst_data[4], int dst_linesizes[4], + const uint8_t *src_data[4], const int src_linesizes[4], + enum AVPixelFormat pix_fmt, int width, int height); + +/** + * Copy image data located in uncacheable (e.g. GPU mapped) memory. Where + * available, this function will use special functionality for reading from such + * memory, which may result in greatly improved performance compared to plain + * av_image_copy(). + * + * The data pointers and the linesizes must be aligned to the maximum required + * by the CPU architecture. + * + * @note The linesize parameters have the type ptrdiff_t here, while they are + * int for av_image_copy(). + * @note On x86, the linesizes currently need to be aligned to the cacheline + * size (i.e. 64) to get improved performance. + */ +void av_image_copy_uc_from(uint8_t *dst_data[4], const ptrdiff_t dst_linesizes[4], + const uint8_t *src_data[4], const ptrdiff_t src_linesizes[4], + enum AVPixelFormat pix_fmt, int width, int height); + +/** + * Setup the data pointers and linesizes based on the specified image + * parameters and the provided array. + * + * The fields of the given image are filled in by using the src + * address which points to the image data buffer. Depending on the + * specified pixel format, one or multiple image data pointers and + * line sizes will be set. If a planar format is specified, several + * pointers will be set pointing to the different picture planes and + * the line sizes of the different planes will be stored in the + * lines_sizes array. Call with src == NULL to get the required + * size for the src buffer. + * + * To allocate the buffer and fill in the dst_data and dst_linesize in + * one call, use av_image_alloc(). + * + * @param dst_data data pointers to be filled in + * @param dst_linesize linesizes for the image in dst_data to be filled in + * @param src buffer which will contain or contains the actual image data, can be NULL + * @param pix_fmt the pixel format of the image + * @param width the width of the image in pixels + * @param height the height of the image in pixels + * @param align the value used in src for linesize alignment + * @return the size in bytes required for src, a negative error code + * in case of failure + */ +int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], + const uint8_t *src, + enum AVPixelFormat pix_fmt, int width, int height, int align); + +/** + * Return the size in bytes of the amount of data required to store an + * image with the given parameters. + * + * @param pix_fmt the pixel format of the image + * @param width the width of the image in pixels + * @param height the height of the image in pixels + * @param align the assumed linesize alignment + * @return the buffer size in bytes, a negative error code in case of failure + */ +int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align); + +/** + * Copy image data from an image into a buffer. + * + * av_image_get_buffer_size() can be used to compute the required size + * for the buffer to fill. + * + * @param dst a buffer into which picture data will be copied + * @param dst_size the size in bytes of dst + * @param src_data pointers containing the source image data + * @param src_linesize linesizes for the image in src_data + * @param pix_fmt the pixel format of the source image + * @param width the width of the source image in pixels + * @param height the height of the source image in pixels + * @param align the assumed linesize alignment for dst + * @return the number of bytes written to dst, or a negative value + * (error code) on error + */ +int av_image_copy_to_buffer(uint8_t *dst, int dst_size, + const uint8_t * const src_data[4], const int src_linesize[4], + enum AVPixelFormat pix_fmt, int width, int height, int align); + +/** + * Check if the given dimension of an image is valid, meaning that all + * bytes of the image can be addressed with a signed int. + * + * @param w the width of the picture + * @param h the height of the picture + * @param log_offset the offset to sum to the log level for logging with log_ctx + * @param log_ctx the parent logging context, it may be NULL + * @return >= 0 if valid, a negative error code otherwise + */ +int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx); + +/** + * Check if the given dimension of an image is valid, meaning that all + * bytes of a plane of an image with the specified pix_fmt can be addressed + * with a signed int. + * + * @param w the width of the picture + * @param h the height of the picture + * @param max_pixels the maximum number of pixels the user wants to accept + * @param pix_fmt the pixel format, can be AV_PIX_FMT_NONE if unknown. + * @param log_offset the offset to sum to the log level for logging with log_ctx + * @param log_ctx the parent logging context, it may be NULL + * @return >= 0 if valid, a negative error code otherwise + */ +int av_image_check_size2(unsigned int w, unsigned int h, int64_t max_pixels, enum AVPixelFormat pix_fmt, int log_offset, void *log_ctx); + +/** + * Check if the given sample aspect ratio of an image is valid. + * + * It is considered invalid if the denominator is 0 or if applying the ratio + * to the image size would make the smaller dimension less than 1. If the + * sar numerator is 0, it is considered unknown and will return as valid. + * + * @param w width of the image + * @param h height of the image + * @param sar sample aspect ratio of the image + * @return 0 if valid, a negative AVERROR code otherwise + */ +int av_image_check_sar(unsigned int w, unsigned int h, AVRational sar); + +/** + * Overwrite the image data with black. This is suitable for filling a + * sub-rectangle of an image, meaning the padding between the right most pixel + * and the left most pixel on the next line will not be overwritten. For some + * formats, the image size might be rounded up due to inherent alignment. + * + * If the pixel format has alpha, the alpha is cleared to opaque. + * + * This can return an error if the pixel format is not supported. Normally, all + * non-hwaccel pixel formats should be supported. + * + * Passing NULL for dst_data is allowed. Then the function returns whether the + * operation would have succeeded. (It can return an error if the pix_fmt is + * not supported.) + * + * @param dst_data data pointers to destination image + * @param dst_linesize linesizes for the destination image + * @param pix_fmt the pixel format of the image + * @param range the color range of the image (important for colorspaces such as YUV) + * @param width the width of the image in pixels + * @param height the height of the image in pixels + * @return 0 if the image data was cleared, a negative AVERROR code otherwise + */ +int av_image_fill_black(uint8_t *dst_data[4], const ptrdiff_t dst_linesize[4], + enum AVPixelFormat pix_fmt, enum AVColorRange range, + int width, int height); + +/** + * @} + */ + + +#endif /* AVUTIL_IMGUTILS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intfloat.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intfloat.h new file mode 100644 index 0000000..fe3d7ec --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intfloat.h
@@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011 Mans Rullgard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_INTFLOAT_H +#define AVUTIL_INTFLOAT_H + +#include <stdint.h> +#include "attributes.h" + +union av_intfloat32 { + uint32_t i; + float f; +}; + +union av_intfloat64 { + uint64_t i; + double f; +}; + +/** + * Reinterpret a 32-bit integer as a float. + */ +static av_always_inline float av_int2float(uint32_t i) +{ + union av_intfloat32 v; + v.i = i; + return v.f; +} + +/** + * Reinterpret a float as a 32-bit integer. + */ +static av_always_inline uint32_t av_float2int(float f) +{ + union av_intfloat32 v; + v.f = f; + return v.i; +} + +/** + * Reinterpret a 64-bit integer as a double. + */ +static av_always_inline double av_int2double(uint64_t i) +{ + union av_intfloat64 v; + v.i = i; + return v.f; +} + +/** + * Reinterpret a double as a 64-bit integer. + */ +static av_always_inline uint64_t av_double2int(double f) +{ + union av_intfloat64 v; + v.f = f; + return v.i; +} + +#endif /* AVUTIL_INTFLOAT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intreadwrite.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intreadwrite.h new file mode 100644 index 0000000..67c763b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intreadwrite.h
@@ -0,0 +1,629 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_INTREADWRITE_H +#define AVUTIL_INTREADWRITE_H + +#include <stdint.h> +#include "libavutil/avconfig.h" +#include "attributes.h" +#include "bswap.h" + +typedef union { + uint64_t u64; + uint32_t u32[2]; + uint16_t u16[4]; + uint8_t u8 [8]; + double f64; + float f32[2]; +} av_alias av_alias64; + +typedef union { + uint32_t u32; + uint16_t u16[2]; + uint8_t u8 [4]; + float f32; +} av_alias av_alias32; + +typedef union { + uint16_t u16; + uint8_t u8 [2]; +} av_alias av_alias16; + +/* + * Arch-specific headers can provide any combination of + * AV_[RW][BLN](16|24|32|48|64) and AV_(COPY|SWAP|ZERO)(64|128) macros. + * Preprocessor symbols must be defined, even if these are implemented + * as inline functions. + * + * R/W means read/write, B/L/N means big/little/native endianness. + * The following macros require aligned access, compared to their + * unaligned variants: AV_(COPY|SWAP|ZERO)(64|128), AV_[RW]N[8-64]A. + * Incorrect usage may range from abysmal performance to crash + * depending on the platform. + * + * The unaligned variants are AV_[RW][BLN][8-64] and AV_COPY*U. + */ + +#ifdef HAVE_AV_CONFIG_H + +#include "config.h" + +#if ARCH_ARM +# include "arm/intreadwrite.h" +#elif ARCH_AVR32 +# include "avr32/intreadwrite.h" +#elif ARCH_MIPS +# include "mips/intreadwrite.h" +#elif ARCH_PPC +# include "ppc/intreadwrite.h" +#elif ARCH_TOMI +# include "tomi/intreadwrite.h" +#elif ARCH_X86 +# include "x86/intreadwrite.h" +#endif + +#endif /* HAVE_AV_CONFIG_H */ + +/* + * Map AV_RNXX <-> AV_R[BL]XX for all variants provided by per-arch headers. + */ + +#if AV_HAVE_BIGENDIAN + +# if defined(AV_RN16) && !defined(AV_RB16) +# define AV_RB16(p) AV_RN16(p) +# elif !defined(AV_RN16) && defined(AV_RB16) +# define AV_RN16(p) AV_RB16(p) +# endif + +# if defined(AV_WN16) && !defined(AV_WB16) +# define AV_WB16(p, v) AV_WN16(p, v) +# elif !defined(AV_WN16) && defined(AV_WB16) +# define AV_WN16(p, v) AV_WB16(p, v) +# endif + +# if defined(AV_RN24) && !defined(AV_RB24) +# define AV_RB24(p) AV_RN24(p) +# elif !defined(AV_RN24) && defined(AV_RB24) +# define AV_RN24(p) AV_RB24(p) +# endif + +# if defined(AV_WN24) && !defined(AV_WB24) +# define AV_WB24(p, v) AV_WN24(p, v) +# elif !defined(AV_WN24) && defined(AV_WB24) +# define AV_WN24(p, v) AV_WB24(p, v) +# endif + +# if defined(AV_RN32) && !defined(AV_RB32) +# define AV_RB32(p) AV_RN32(p) +# elif !defined(AV_RN32) && defined(AV_RB32) +# define AV_RN32(p) AV_RB32(p) +# endif + +# if defined(AV_WN32) && !defined(AV_WB32) +# define AV_WB32(p, v) AV_WN32(p, v) +# elif !defined(AV_WN32) && defined(AV_WB32) +# define AV_WN32(p, v) AV_WB32(p, v) +# endif + +# if defined(AV_RN48) && !defined(AV_RB48) +# define AV_RB48(p) AV_RN48(p) +# elif !defined(AV_RN48) && defined(AV_RB48) +# define AV_RN48(p) AV_RB48(p) +# endif + +# if defined(AV_WN48) && !defined(AV_WB48) +# define AV_WB48(p, v) AV_WN48(p, v) +# elif !defined(AV_WN48) && defined(AV_WB48) +# define AV_WN48(p, v) AV_WB48(p, v) +# endif + +# if defined(AV_RN64) && !defined(AV_RB64) +# define AV_RB64(p) AV_RN64(p) +# elif !defined(AV_RN64) && defined(AV_RB64) +# define AV_RN64(p) AV_RB64(p) +# endif + +# if defined(AV_WN64) && !defined(AV_WB64) +# define AV_WB64(p, v) AV_WN64(p, v) +# elif !defined(AV_WN64) && defined(AV_WB64) +# define AV_WN64(p, v) AV_WB64(p, v) +# endif + +#else /* AV_HAVE_BIGENDIAN */ + +# if defined(AV_RN16) && !defined(AV_RL16) +# define AV_RL16(p) AV_RN16(p) +# elif !defined(AV_RN16) && defined(AV_RL16) +# define AV_RN16(p) AV_RL16(p) +# endif + +# if defined(AV_WN16) && !defined(AV_WL16) +# define AV_WL16(p, v) AV_WN16(p, v) +# elif !defined(AV_WN16) && defined(AV_WL16) +# define AV_WN16(p, v) AV_WL16(p, v) +# endif + +# if defined(AV_RN24) && !defined(AV_RL24) +# define AV_RL24(p) AV_RN24(p) +# elif !defined(AV_RN24) && defined(AV_RL24) +# define AV_RN24(p) AV_RL24(p) +# endif + +# if defined(AV_WN24) && !defined(AV_WL24) +# define AV_WL24(p, v) AV_WN24(p, v) +# elif !defined(AV_WN24) && defined(AV_WL24) +# define AV_WN24(p, v) AV_WL24(p, v) +# endif + +# if defined(AV_RN32) && !defined(AV_RL32) +# define AV_RL32(p) AV_RN32(p) +# elif !defined(AV_RN32) && defined(AV_RL32) +# define AV_RN32(p) AV_RL32(p) +# endif + +# if defined(AV_WN32) && !defined(AV_WL32) +# define AV_WL32(p, v) AV_WN32(p, v) +# elif !defined(AV_WN32) && defined(AV_WL32) +# define AV_WN32(p, v) AV_WL32(p, v) +# endif + +# if defined(AV_RN48) && !defined(AV_RL48) +# define AV_RL48(p) AV_RN48(p) +# elif !defined(AV_RN48) && defined(AV_RL48) +# define AV_RN48(p) AV_RL48(p) +# endif + +# if defined(AV_WN48) && !defined(AV_WL48) +# define AV_WL48(p, v) AV_WN48(p, v) +# elif !defined(AV_WN48) && defined(AV_WL48) +# define AV_WN48(p, v) AV_WL48(p, v) +# endif + +# if defined(AV_RN64) && !defined(AV_RL64) +# define AV_RL64(p) AV_RN64(p) +# elif !defined(AV_RN64) && defined(AV_RL64) +# define AV_RN64(p) AV_RL64(p) +# endif + +# if defined(AV_WN64) && !defined(AV_WL64) +# define AV_WL64(p, v) AV_WN64(p, v) +# elif !defined(AV_WN64) && defined(AV_WL64) +# define AV_WN64(p, v) AV_WL64(p, v) +# endif + +#endif /* !AV_HAVE_BIGENDIAN */ + +/* + * Define AV_[RW]N helper macros to simplify definitions not provided + * by per-arch headers. + */ + +#if defined(__GNUC__) + +union unaligned_64 { uint64_t l; } __attribute__((packed)) av_alias; +union unaligned_32 { uint32_t l; } __attribute__((packed)) av_alias; +union unaligned_16 { uint16_t l; } __attribute__((packed)) av_alias; + +# define AV_RN(s, p) (((const union unaligned_##s *) (p))->l) +# define AV_WN(s, p, v) ((((union unaligned_##s *) (p))->l) = (v)) + +#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_X64) || defined(_M_ARM64)) && AV_HAVE_FAST_UNALIGNED + +# define AV_RN(s, p) (*((const __unaligned uint##s##_t*)(p))) +# define AV_WN(s, p, v) (*((__unaligned uint##s##_t*)(p)) = (v)) + +#elif AV_HAVE_FAST_UNALIGNED + +# define AV_RN(s, p) (((const av_alias##s*)(p))->u##s) +# define AV_WN(s, p, v) (((av_alias##s*)(p))->u##s = (v)) + +#else + +#ifndef AV_RB16 +# define AV_RB16(x) \ + ((((const uint8_t*)(x))[0] << 8) | \ + ((const uint8_t*)(x))[1]) +#endif +#ifndef AV_WB16 +# define AV_WB16(p, val) do { \ + uint16_t d = (val); \ + ((uint8_t*)(p))[1] = (d); \ + ((uint8_t*)(p))[0] = (d)>>8; \ + } while(0) +#endif + +#ifndef AV_RL16 +# define AV_RL16(x) \ + ((((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL16 +# define AV_WL16(p, val) do { \ + uint16_t d = (val); \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + } while(0) +#endif + +#ifndef AV_RB32 +# define AV_RB32(x) \ + (((uint32_t)((const uint8_t*)(x))[0] << 24) | \ + (((const uint8_t*)(x))[1] << 16) | \ + (((const uint8_t*)(x))[2] << 8) | \ + ((const uint8_t*)(x))[3]) +#endif +#ifndef AV_WB32 +# define AV_WB32(p, val) do { \ + uint32_t d = (val); \ + ((uint8_t*)(p))[3] = (d); \ + ((uint8_t*)(p))[2] = (d)>>8; \ + ((uint8_t*)(p))[1] = (d)>>16; \ + ((uint8_t*)(p))[0] = (d)>>24; \ + } while(0) +#endif + +#ifndef AV_RL32 +# define AV_RL32(x) \ + (((uint32_t)((const uint8_t*)(x))[3] << 24) | \ + (((const uint8_t*)(x))[2] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL32 +# define AV_WL32(p, val) do { \ + uint32_t d = (val); \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[2] = (d)>>16; \ + ((uint8_t*)(p))[3] = (d)>>24; \ + } while(0) +#endif + +#ifndef AV_RB64 +# define AV_RB64(x) \ + (((uint64_t)((const uint8_t*)(x))[0] << 56) | \ + ((uint64_t)((const uint8_t*)(x))[1] << 48) | \ + ((uint64_t)((const uint8_t*)(x))[2] << 40) | \ + ((uint64_t)((const uint8_t*)(x))[3] << 32) | \ + ((uint64_t)((const uint8_t*)(x))[4] << 24) | \ + ((uint64_t)((const uint8_t*)(x))[5] << 16) | \ + ((uint64_t)((const uint8_t*)(x))[6] << 8) | \ + (uint64_t)((const uint8_t*)(x))[7]) +#endif +#ifndef AV_WB64 +# define AV_WB64(p, val) do { \ + uint64_t d = (val); \ + ((uint8_t*)(p))[7] = (d); \ + ((uint8_t*)(p))[6] = (d)>>8; \ + ((uint8_t*)(p))[5] = (d)>>16; \ + ((uint8_t*)(p))[4] = (d)>>24; \ + ((uint8_t*)(p))[3] = (d)>>32; \ + ((uint8_t*)(p))[2] = (d)>>40; \ + ((uint8_t*)(p))[1] = (d)>>48; \ + ((uint8_t*)(p))[0] = (d)>>56; \ + } while(0) +#endif + +#ifndef AV_RL64 +# define AV_RL64(x) \ + (((uint64_t)((const uint8_t*)(x))[7] << 56) | \ + ((uint64_t)((const uint8_t*)(x))[6] << 48) | \ + ((uint64_t)((const uint8_t*)(x))[5] << 40) | \ + ((uint64_t)((const uint8_t*)(x))[4] << 32) | \ + ((uint64_t)((const uint8_t*)(x))[3] << 24) | \ + ((uint64_t)((const uint8_t*)(x))[2] << 16) | \ + ((uint64_t)((const uint8_t*)(x))[1] << 8) | \ + (uint64_t)((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL64 +# define AV_WL64(p, val) do { \ + uint64_t d = (val); \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[2] = (d)>>16; \ + ((uint8_t*)(p))[3] = (d)>>24; \ + ((uint8_t*)(p))[4] = (d)>>32; \ + ((uint8_t*)(p))[5] = (d)>>40; \ + ((uint8_t*)(p))[6] = (d)>>48; \ + ((uint8_t*)(p))[7] = (d)>>56; \ + } while(0) +#endif + +#if AV_HAVE_BIGENDIAN +# define AV_RN(s, p) AV_RB##s(p) +# define AV_WN(s, p, v) AV_WB##s(p, v) +#else +# define AV_RN(s, p) AV_RL##s(p) +# define AV_WN(s, p, v) AV_WL##s(p, v) +#endif + +#endif /* HAVE_FAST_UNALIGNED */ + +#ifndef AV_RN16 +# define AV_RN16(p) AV_RN(16, p) +#endif + +#ifndef AV_RN32 +# define AV_RN32(p) AV_RN(32, p) +#endif + +#ifndef AV_RN64 +# define AV_RN64(p) AV_RN(64, p) +#endif + +#ifndef AV_WN16 +# define AV_WN16(p, v) AV_WN(16, p, v) +#endif + +#ifndef AV_WN32 +# define AV_WN32(p, v) AV_WN(32, p, v) +#endif + +#ifndef AV_WN64 +# define AV_WN64(p, v) AV_WN(64, p, v) +#endif + +#if AV_HAVE_BIGENDIAN +# define AV_RB(s, p) AV_RN##s(p) +# define AV_WB(s, p, v) AV_WN##s(p, v) +# define AV_RL(s, p) av_bswap##s(AV_RN##s(p)) +# define AV_WL(s, p, v) AV_WN##s(p, av_bswap##s(v)) +#else +# define AV_RB(s, p) av_bswap##s(AV_RN##s(p)) +# define AV_WB(s, p, v) AV_WN##s(p, av_bswap##s(v)) +# define AV_RL(s, p) AV_RN##s(p) +# define AV_WL(s, p, v) AV_WN##s(p, v) +#endif + +#define AV_RB8(x) (((const uint8_t*)(x))[0]) +#define AV_WB8(p, d) do { ((uint8_t*)(p))[0] = (d); } while(0) + +#define AV_RL8(x) AV_RB8(x) +#define AV_WL8(p, d) AV_WB8(p, d) + +#ifndef AV_RB16 +# define AV_RB16(p) AV_RB(16, p) +#endif +#ifndef AV_WB16 +# define AV_WB16(p, v) AV_WB(16, p, v) +#endif + +#ifndef AV_RL16 +# define AV_RL16(p) AV_RL(16, p) +#endif +#ifndef AV_WL16 +# define AV_WL16(p, v) AV_WL(16, p, v) +#endif + +#ifndef AV_RB32 +# define AV_RB32(p) AV_RB(32, p) +#endif +#ifndef AV_WB32 +# define AV_WB32(p, v) AV_WB(32, p, v) +#endif + +#ifndef AV_RL32 +# define AV_RL32(p) AV_RL(32, p) +#endif +#ifndef AV_WL32 +# define AV_WL32(p, v) AV_WL(32, p, v) +#endif + +#ifndef AV_RB64 +# define AV_RB64(p) AV_RB(64, p) +#endif +#ifndef AV_WB64 +# define AV_WB64(p, v) AV_WB(64, p, v) +#endif + +#ifndef AV_RL64 +# define AV_RL64(p) AV_RL(64, p) +#endif +#ifndef AV_WL64 +# define AV_WL64(p, v) AV_WL(64, p, v) +#endif + +#ifndef AV_RB24 +# define AV_RB24(x) \ + ((((const uint8_t*)(x))[0] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[2]) +#endif +#ifndef AV_WB24 +# define AV_WB24(p, d) do { \ + ((uint8_t*)(p))[2] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[0] = (d)>>16; \ + } while(0) +#endif + +#ifndef AV_RL24 +# define AV_RL24(x) \ + ((((const uint8_t*)(x))[2] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL24 +# define AV_WL24(p, d) do { \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[2] = (d)>>16; \ + } while(0) +#endif + +#ifndef AV_RB48 +# define AV_RB48(x) \ + (((uint64_t)((const uint8_t*)(x))[0] << 40) | \ + ((uint64_t)((const uint8_t*)(x))[1] << 32) | \ + ((uint64_t)((const uint8_t*)(x))[2] << 24) | \ + ((uint64_t)((const uint8_t*)(x))[3] << 16) | \ + ((uint64_t)((const uint8_t*)(x))[4] << 8) | \ + (uint64_t)((const uint8_t*)(x))[5]) +#endif +#ifndef AV_WB48 +# define AV_WB48(p, darg) do { \ + uint64_t d = (darg); \ + ((uint8_t*)(p))[5] = (d); \ + ((uint8_t*)(p))[4] = (d)>>8; \ + ((uint8_t*)(p))[3] = (d)>>16; \ + ((uint8_t*)(p))[2] = (d)>>24; \ + ((uint8_t*)(p))[1] = (d)>>32; \ + ((uint8_t*)(p))[0] = (d)>>40; \ + } while(0) +#endif + +#ifndef AV_RL48 +# define AV_RL48(x) \ + (((uint64_t)((const uint8_t*)(x))[5] << 40) | \ + ((uint64_t)((const uint8_t*)(x))[4] << 32) | \ + ((uint64_t)((const uint8_t*)(x))[3] << 24) | \ + ((uint64_t)((const uint8_t*)(x))[2] << 16) | \ + ((uint64_t)((const uint8_t*)(x))[1] << 8) | \ + (uint64_t)((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL48 +# define AV_WL48(p, darg) do { \ + uint64_t d = (darg); \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[2] = (d)>>16; \ + ((uint8_t*)(p))[3] = (d)>>24; \ + ((uint8_t*)(p))[4] = (d)>>32; \ + ((uint8_t*)(p))[5] = (d)>>40; \ + } while(0) +#endif + +/* + * The AV_[RW]NA macros access naturally aligned data + * in a type-safe way. + */ + +#define AV_RNA(s, p) (((const av_alias##s*)(p))->u##s) +#define AV_WNA(s, p, v) (((av_alias##s*)(p))->u##s = (v)) + +#ifndef AV_RN16A +# define AV_RN16A(p) AV_RNA(16, p) +#endif + +#ifndef AV_RN32A +# define AV_RN32A(p) AV_RNA(32, p) +#endif + +#ifndef AV_RN64A +# define AV_RN64A(p) AV_RNA(64, p) +#endif + +#ifndef AV_WN16A +# define AV_WN16A(p, v) AV_WNA(16, p, v) +#endif + +#ifndef AV_WN32A +# define AV_WN32A(p, v) AV_WNA(32, p, v) +#endif + +#ifndef AV_WN64A +# define AV_WN64A(p, v) AV_WNA(64, p, v) +#endif + +/* + * The AV_COPYxxU macros are suitable for copying data to/from unaligned + * memory locations. + */ + +#define AV_COPYU(n, d, s) AV_WN##n(d, AV_RN##n(s)); + +#ifndef AV_COPY16U +# define AV_COPY16U(d, s) AV_COPYU(16, d, s) +#endif + +#ifndef AV_COPY32U +# define AV_COPY32U(d, s) AV_COPYU(32, d, s) +#endif + +#ifndef AV_COPY64U +# define AV_COPY64U(d, s) AV_COPYU(64, d, s) +#endif + +#ifndef AV_COPY128U +# define AV_COPY128U(d, s) \ + do { \ + AV_COPY64U(d, s); \ + AV_COPY64U((char *)(d) + 8, (const char *)(s) + 8); \ + } while(0) +#endif + +/* Parameters for AV_COPY*, AV_SWAP*, AV_ZERO* must be + * naturally aligned. They may be implemented using MMX, + * so emms_c() must be called before using any float code + * afterwards. + */ + +#define AV_COPY(n, d, s) \ + (((av_alias##n*)(d))->u##n = ((const av_alias##n*)(s))->u##n) + +#ifndef AV_COPY16 +# define AV_COPY16(d, s) AV_COPY(16, d, s) +#endif + +#ifndef AV_COPY32 +# define AV_COPY32(d, s) AV_COPY(32, d, s) +#endif + +#ifndef AV_COPY64 +# define AV_COPY64(d, s) AV_COPY(64, d, s) +#endif + +#ifndef AV_COPY128 +# define AV_COPY128(d, s) \ + do { \ + AV_COPY64(d, s); \ + AV_COPY64((char*)(d)+8, (char*)(s)+8); \ + } while(0) +#endif + +#define AV_SWAP(n, a, b) FFSWAP(av_alias##n, *(av_alias##n*)(a), *(av_alias##n*)(b)) + +#ifndef AV_SWAP64 +# define AV_SWAP64(a, b) AV_SWAP(64, a, b) +#endif + +#define AV_ZERO(n, d) (((av_alias##n*)(d))->u##n = 0) + +#ifndef AV_ZERO16 +# define AV_ZERO16(d) AV_ZERO(16, d) +#endif + +#ifndef AV_ZERO32 +# define AV_ZERO32(d) AV_ZERO(32, d) +#endif + +#ifndef AV_ZERO64 +# define AV_ZERO64(d) AV_ZERO(64, d) +#endif + +#ifndef AV_ZERO128 +# define AV_ZERO128(d) \ + do { \ + AV_ZERO64(d); \ + AV_ZERO64((char*)(d)+8); \ + } while(0) +#endif + +#endif /* AVUTIL_INTREADWRITE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lfg.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lfg.h new file mode 100644 index 0000000..03f779a --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lfg.h
@@ -0,0 +1,71 @@ +/* + * Lagged Fibonacci PRNG + * Copyright (c) 2008 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_LFG_H +#define AVUTIL_LFG_H + +#include <stdint.h> + +typedef struct AVLFG { + unsigned int state[64]; + int index; +} AVLFG; + +void av_lfg_init(AVLFG *c, unsigned int seed); + +/** + * Seed the state of the ALFG using binary data. + * + * Return value: 0 on success, negative value (AVERROR) on failure. + */ +int av_lfg_init_from_data(AVLFG *c, const uint8_t *data, unsigned int length); + +/** + * Get the next random unsigned 32-bit number using an ALFG. + * + * Please also consider a simple LCG like state= state*1664525+1013904223, + * it may be good enough and faster for your specific use case. + */ +static inline unsigned int av_lfg_get(AVLFG *c){ + c->state[c->index & 63] = c->state[(c->index-24) & 63] + c->state[(c->index-55) & 63]; + return c->state[c->index++ & 63]; +} + +/** + * Get the next random unsigned 32-bit number using a MLFG. + * + * Please also consider av_lfg_get() above, it is faster. + */ +static inline unsigned int av_mlfg_get(AVLFG *c){ + unsigned int a= c->state[(c->index-55) & 63]; + unsigned int b= c->state[(c->index-24) & 63]; + return c->state[c->index++ & 63] = 2*a*b+a+b; +} + +/** + * Get the next two numbers generated by a Box-Muller Gaussian + * generator using the random numbers issued by lfg. + * + * @param out array where the two generated numbers are placed + */ +void av_bmg_get(AVLFG *lfg, double out[2]); + +#endif /* AVUTIL_LFG_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/log.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/log.h new file mode 100644 index 0000000..d9554e6 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/log.h
@@ -0,0 +1,362 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_LOG_H +#define AVUTIL_LOG_H + +#include <stdarg.h> +#include "avutil.h" +#include "attributes.h" +#include "version.h" + +typedef enum { + AV_CLASS_CATEGORY_NA = 0, + AV_CLASS_CATEGORY_INPUT, + AV_CLASS_CATEGORY_OUTPUT, + AV_CLASS_CATEGORY_MUXER, + AV_CLASS_CATEGORY_DEMUXER, + AV_CLASS_CATEGORY_ENCODER, + AV_CLASS_CATEGORY_DECODER, + AV_CLASS_CATEGORY_FILTER, + AV_CLASS_CATEGORY_BITSTREAM_FILTER, + AV_CLASS_CATEGORY_SWSCALER, + AV_CLASS_CATEGORY_SWRESAMPLER, + AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40, + AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, + AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, + AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, + AV_CLASS_CATEGORY_DEVICE_OUTPUT, + AV_CLASS_CATEGORY_DEVICE_INPUT, + AV_CLASS_CATEGORY_NB ///< not part of ABI/API +}AVClassCategory; + +#define AV_IS_INPUT_DEVICE(category) \ + (((category) == AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT) || \ + ((category) == AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT) || \ + ((category) == AV_CLASS_CATEGORY_DEVICE_INPUT)) + +#define AV_IS_OUTPUT_DEVICE(category) \ + (((category) == AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT) || \ + ((category) == AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT) || \ + ((category) == AV_CLASS_CATEGORY_DEVICE_OUTPUT)) + +struct AVOptionRanges; + +/** + * Describe the class of an AVClass context structure. That is an + * arbitrary struct of which the first field is a pointer to an + * AVClass struct (e.g. AVCodecContext, AVFormatContext etc.). + */ +typedef struct AVClass { + /** + * The name of the class; usually it is the same name as the + * context structure type to which the AVClass is associated. + */ + const char* class_name; + + /** + * A pointer to a function which returns the name of a context + * instance ctx associated with the class. + */ + const char* (*item_name)(void* ctx); + + /** + * a pointer to the first option specified in the class if any or NULL + * + * @see av_set_default_options() + */ + const struct AVOption *option; + + /** + * LIBAVUTIL_VERSION with which this structure was created. + * This is used to allow fields to be added without requiring major + * version bumps everywhere. + */ + + int version; + + /** + * Offset in the structure where log_level_offset is stored. + * 0 means there is no such variable + */ + int log_level_offset_offset; + + /** + * Offset in the structure where a pointer to the parent context for + * logging is stored. For example a decoder could pass its AVCodecContext + * to eval as such a parent context, which an av_log() implementation + * could then leverage to display the parent context. + * The offset can be NULL. + */ + int parent_log_context_offset; + + /** + * Return next AVOptions-enabled child or NULL + */ + void* (*child_next)(void *obj, void *prev); + + /** + * Return an AVClass corresponding to the next potential + * AVOptions-enabled child. + * + * The difference between child_next and this is that + * child_next iterates over _already existing_ objects, while + * child_class_next iterates over _all possible_ children. + */ + const struct AVClass* (*child_class_next)(const struct AVClass *prev); + + /** + * Category used for visualization (like color) + * This is only set if the category is equal for all objects using this class. + * available since version (51 << 16 | 56 << 8 | 100) + */ + AVClassCategory category; + + /** + * Callback to return the category. + * available since version (51 << 16 | 59 << 8 | 100) + */ + AVClassCategory (*get_category)(void* ctx); + + /** + * Callback to return the supported/allowed ranges. + * available since version (52.12) + */ + int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags); +} AVClass; + +/** + * @addtogroup lavu_log + * + * @{ + * + * @defgroup lavu_log_constants Logging Constants + * + * @{ + */ + +/** + * Print no output. + */ +#define AV_LOG_QUIET -8 + +/** + * Something went really wrong and we will crash now. + */ +#define AV_LOG_PANIC 0 + +/** + * Something went wrong and recovery is not possible. + * For example, no header was found for a format which depends + * on headers or an illegal combination of parameters is used. + */ +#define AV_LOG_FATAL 8 + +/** + * Something went wrong and cannot losslessly be recovered. + * However, not all future data is affected. + */ +#define AV_LOG_ERROR 16 + +/** + * Something somehow does not look correct. This may or may not + * lead to problems. An example would be the use of '-vstrict -2'. + */ +#define AV_LOG_WARNING 24 + +/** + * Standard information. + */ +#define AV_LOG_INFO 32 + +/** + * Detailed information. + */ +#define AV_LOG_VERBOSE 40 + +/** + * Stuff which is only useful for libav* developers. + */ +#define AV_LOG_DEBUG 48 + +/** + * Extremely verbose debugging, useful for libav* development. + */ +#define AV_LOG_TRACE 56 + +#define AV_LOG_MAX_OFFSET (AV_LOG_TRACE - AV_LOG_QUIET) + +/** + * @} + */ + +/** + * Sets additional colors for extended debugging sessions. + * @code + av_log(ctx, AV_LOG_DEBUG|AV_LOG_C(134), "Message in purple\n"); + @endcode + * Requires 256color terminal support. Uses outside debugging is not + * recommended. + */ +#define AV_LOG_C(x) ((x) << 8) + +/** + * Send the specified message to the log if the level is less than or equal + * to the current av_log_level. By default, all logging messages are sent to + * stderr. This behavior can be altered by setting a different logging callback + * function. + * @see av_log_set_callback + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct or NULL if general log. + * @param level The importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant". + * @param fmt The format string (printf-compatible) that specifies how + * subsequent arguments are converted to output. + */ +void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4); + + +/** + * Send the specified message to the log if the level is less than or equal + * to the current av_log_level. By default, all logging messages are sent to + * stderr. This behavior can be altered by setting a different logging callback + * function. + * @see av_log_set_callback + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant". + * @param fmt The format string (printf-compatible) that specifies how + * subsequent arguments are converted to output. + * @param vl The arguments referenced by the format string. + */ +void av_vlog(void *avcl, int level, const char *fmt, va_list vl); + +/** + * Get the current log level + * + * @see lavu_log_constants + * + * @return Current log level + */ +int av_log_get_level(void); + +/** + * Set the log level + * + * @see lavu_log_constants + * + * @param level Logging level + */ +void av_log_set_level(int level); + +/** + * Set the logging callback + * + * @note The callback must be thread safe, even if the application does not use + * threads itself as some codecs are multithreaded. + * + * @see av_log_default_callback + * + * @param callback A logging function with a compatible signature. + */ +void av_log_set_callback(void (*callback)(void*, int, const char*, va_list)); + +/** + * Default logging callback + * + * It prints the message to stderr, optionally colorizing it. + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant". + * @param fmt The format string (printf-compatible) that specifies how + * subsequent arguments are converted to output. + * @param vl The arguments referenced by the format string. + */ +void av_log_default_callback(void *avcl, int level, const char *fmt, + va_list vl); + +/** + * Return the context name + * + * @param ctx The AVClass context + * + * @return The AVClass class_name + */ +const char* av_default_item_name(void* ctx); +AVClassCategory av_default_get_category(void *ptr); + +/** + * Format a line of log the same way as the default callback. + * @param line buffer to receive the formatted line + * @param line_size size of the buffer + * @param print_prefix used to store whether the prefix must be printed; + * must point to a persistent integer initially set to 1 + */ +void av_log_format_line(void *ptr, int level, const char *fmt, va_list vl, + char *line, int line_size, int *print_prefix); + +/** + * Format a line of log the same way as the default callback. + * @param line buffer to receive the formatted line; + * may be NULL if line_size is 0 + * @param line_size size of the buffer; at most line_size-1 characters will + * be written to the buffer, plus one null terminator + * @param print_prefix used to store whether the prefix must be printed; + * must point to a persistent integer initially set to 1 + * @return Returns a negative value if an error occurred, otherwise returns + * the number of characters that would have been written for a + * sufficiently large buffer, not including the terminating null + * character. If the return value is not less than line_size, it means + * that the log message was truncated to fit the buffer. + */ +int av_log_format_line2(void *ptr, int level, const char *fmt, va_list vl, + char *line, int line_size, int *print_prefix); + +/** + * Skip repeated messages, this requires the user app to use av_log() instead of + * (f)printf as the 2 would otherwise interfere and lead to + * "Last message repeated x times" messages below (f)printf messages with some + * bad luck. + * Also to receive the last, "last repeated" line if any, the user app must + * call av_log(NULL, AV_LOG_QUIET, "%s", ""); at the end + */ +#define AV_LOG_SKIP_REPEATED 1 + +/** + * Include the log severity in messages originating from codecs. + * + * Results in messages such as: + * [rawvideo @ 0xDEADBEEF] [error] encode did not produce valid pts + */ +#define AV_LOG_PRINT_LEVEL 2 + +void av_log_set_flags(int arg); +int av_log_get_flags(void); + +/** + * @} + */ + +#endif /* AVUTIL_LOG_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lzo.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lzo.h new file mode 100644 index 0000000..c034039 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lzo.h
@@ -0,0 +1,66 @@ +/* + * LZO 1x decompression + * copyright (c) 2006 Reimar Doeffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_LZO_H +#define AVUTIL_LZO_H + +/** + * @defgroup lavu_lzo LZO + * @ingroup lavu_crypto + * + * @{ + */ + +#include <stdint.h> + +/** @name Error flags returned by av_lzo1x_decode + * @{ */ +/// end of the input buffer reached before decoding finished +#define AV_LZO_INPUT_DEPLETED 1 +/// decoded data did not fit into output buffer +#define AV_LZO_OUTPUT_FULL 2 +/// a reference to previously decoded data was wrong +#define AV_LZO_INVALID_BACKPTR 4 +/// a non-specific error in the compressed bitstream +#define AV_LZO_ERROR 8 +/** @} */ + +#define AV_LZO_INPUT_PADDING 8 +#define AV_LZO_OUTPUT_PADDING 12 + +/** + * @brief Decodes LZO 1x compressed data. + * @param out output buffer + * @param outlen size of output buffer, number of bytes left are returned here + * @param in input buffer + * @param inlen size of input buffer, number of bytes left are returned here + * @return 0 on success, otherwise a combination of the error flags above + * + * Make sure all buffers are appropriately padded, in must provide + * AV_LZO_INPUT_PADDING, out must provide AV_LZO_OUTPUT_PADDING additional bytes. + */ +int av_lzo1x_decode(void *out, int *outlen, const void *in, int *inlen); + +/** + * @} + */ + +#endif /* AVUTIL_LZO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/macros.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/macros.h new file mode 100644 index 0000000..2007ee5 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/macros.h
@@ -0,0 +1,50 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu + * Utility Preprocessor macros + */ + +#ifndef AVUTIL_MACROS_H +#define AVUTIL_MACROS_H + +/** + * @addtogroup preproc_misc Preprocessor String Macros + * + * String manipulation macros + * + * @{ + */ + +#define AV_STRINGIFY(s) AV_TOSTRING(s) +#define AV_TOSTRING(s) #s + +#define AV_GLUE(a, b) a ## b +#define AV_JOIN(a, b) AV_GLUE(a, b) + +/** + * @} + */ + +#define AV_PRAGMA(s) _Pragma(#s) + +#define FFALIGN(x, a) (((x)+(a)-1)&~((a)-1)) + +#endif /* AVUTIL_MACROS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mastering_display_metadata.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mastering_display_metadata.h new file mode 100644 index 0000000..c23b07c --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mastering_display_metadata.h
@@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016 Neil Birkbeck <neil.birkbeck@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_MASTERING_DISPLAY_METADATA_H +#define AVUTIL_MASTERING_DISPLAY_METADATA_H + +#include "frame.h" +#include "rational.h" + + +/** + * Mastering display metadata capable of representing the color volume of + * the display used to master the content (SMPTE 2086:2014). + * + * To be used as payload of a AVFrameSideData or AVPacketSideData with the + * appropriate type. + * + * @note The struct should be allocated with av_mastering_display_metadata_alloc() + * and its size is not a part of the public ABI. + */ +typedef struct AVMasteringDisplayMetadata { + /** + * CIE 1931 xy chromaticity coords of color primaries (r, g, b order). + */ + AVRational display_primaries[3][2]; + + /** + * CIE 1931 xy chromaticity coords of white point. + */ + AVRational white_point[2]; + + /** + * Min luminance of mastering display (cd/m^2). + */ + AVRational min_luminance; + + /** + * Max luminance of mastering display (cd/m^2). + */ + AVRational max_luminance; + + /** + * Flag indicating whether the display primaries (and white point) are set. + */ + int has_primaries; + + /** + * Flag indicating whether the luminance (min_ and max_) have been set. + */ + int has_luminance; + +} AVMasteringDisplayMetadata; + +/** + * Allocate an AVMasteringDisplayMetadata structure and set its fields to + * default values. The resulting struct can be freed using av_freep(). + * + * @return An AVMasteringDisplayMetadata filled with default values or NULL + * on failure. + */ +AVMasteringDisplayMetadata *av_mastering_display_metadata_alloc(void); + +/** + * Allocate a complete AVMasteringDisplayMetadata and add it to the frame. + * + * @param frame The frame which side data is added to. + * + * @return The AVMasteringDisplayMetadata structure to be filled by caller. + */ +AVMasteringDisplayMetadata *av_mastering_display_metadata_create_side_data(AVFrame *frame); + +/** + * Content light level needed by to transmit HDR over HDMI (CTA-861.3). + * + * To be used as payload of a AVFrameSideData or AVPacketSideData with the + * appropriate type. + * + * @note The struct should be allocated with av_content_light_metadata_alloc() + * and its size is not a part of the public ABI. + */ +typedef struct AVContentLightMetadata { + /** + * Max content light level (cd/m^2). + */ + unsigned MaxCLL; + + /** + * Max average light level per frame (cd/m^2). + */ + unsigned MaxFALL; +} AVContentLightMetadata; + +/** + * Allocate an AVContentLightMetadata structure and set its fields to + * default values. The resulting struct can be freed using av_freep(). + * + * @return An AVContentLightMetadata filled with default values or NULL + * on failure. + */ +AVContentLightMetadata *av_content_light_metadata_alloc(size_t *size); + +/** + * Allocate a complete AVContentLightMetadata and add it to the frame. + * + * @param frame The frame which side data is added to. + * + * @return The AVContentLightMetadata structure to be filled by caller. + */ +AVContentLightMetadata *av_content_light_metadata_create_side_data(AVFrame *frame); + +#endif /* AVUTIL_MASTERING_DISPLAY_METADATA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mathematics.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mathematics.h new file mode 100644 index 0000000..5490180 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mathematics.h
@@ -0,0 +1,242 @@ +/* + * copyright (c) 2005-2012 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @addtogroup lavu_math + * Mathematical utilities for working with timestamp and time base. + */ + +#ifndef AVUTIL_MATHEMATICS_H +#define AVUTIL_MATHEMATICS_H + +#include <stdint.h> +#include <math.h> +#include "attributes.h" +#include "rational.h" +#include "intfloat.h" + +#ifndef M_E +#define M_E 2.7182818284590452354 /* e */ +#endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 /* log_e 2 */ +#endif +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 /* log_e 10 */ +#endif +#ifndef M_LOG2_10 +#define M_LOG2_10 3.32192809488736234787 /* log_2 10 */ +#endif +#ifndef M_PHI +#define M_PHI 1.61803398874989484820 /* phi / golden ratio */ +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#endif +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ +#endif +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#endif +#ifndef NAN +#define NAN av_int2float(0x7fc00000) +#endif +#ifndef INFINITY +#define INFINITY av_int2float(0x7f800000) +#endif + +/** + * @addtogroup lavu_math + * + * @{ + */ + +/** + * Rounding methods. + */ +enum AVRounding { + AV_ROUND_ZERO = 0, ///< Round toward zero. + AV_ROUND_INF = 1, ///< Round away from zero. + AV_ROUND_DOWN = 2, ///< Round toward -infinity. + AV_ROUND_UP = 3, ///< Round toward +infinity. + AV_ROUND_NEAR_INF = 5, ///< Round to nearest and halfway cases away from zero. + /** + * Flag telling rescaling functions to pass `INT64_MIN`/`MAX` through + * unchanged, avoiding special cases for #AV_NOPTS_VALUE. + * + * Unlike other values of the enumeration AVRounding, this value is a + * bitmask that must be used in conjunction with another value of the + * enumeration through a bitwise OR, in order to set behavior for normal + * cases. + * + * @code{.c} + * av_rescale_rnd(3, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + * // Rescaling 3: + * // Calculating 3 * 1 / 2 + * // 3 / 2 is rounded up to 2 + * // => 2 + * + * av_rescale_rnd(AV_NOPTS_VALUE, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + * // Rescaling AV_NOPTS_VALUE: + * // AV_NOPTS_VALUE == INT64_MIN + * // AV_NOPTS_VALUE is passed through + * // => AV_NOPTS_VALUE + * @endcode + */ + AV_ROUND_PASS_MINMAX = 8192, +}; + +/** + * Compute the greatest common divisor of two integer operands. + * + * @param a,b Operands + * @return GCD of a and b up to sign; if a >= 0 and b >= 0, return value is >= 0; + * if a == 0 and b == 0, returns 0. + */ +int64_t av_const av_gcd(int64_t a, int64_t b); + +/** + * Rescale a 64-bit integer with rounding to nearest. + * + * The operation is mathematically equivalent to `a * b / c`, but writing that + * directly can overflow. + * + * This function is equivalent to av_rescale_rnd() with #AV_ROUND_NEAR_INF. + * + * @see av_rescale_rnd(), av_rescale_q(), av_rescale_q_rnd() + */ +int64_t av_rescale(int64_t a, int64_t b, int64_t c) av_const; + +/** + * Rescale a 64-bit integer with specified rounding. + * + * The operation is mathematically equivalent to `a * b / c`, but writing that + * directly can overflow, and does not support different rounding methods. + * + * @see av_rescale(), av_rescale_q(), av_rescale_q_rnd() + */ +int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) av_const; + +/** + * Rescale a 64-bit integer by 2 rational numbers. + * + * The operation is mathematically equivalent to `a * bq / cq`. + * + * This function is equivalent to av_rescale_q_rnd() with #AV_ROUND_NEAR_INF. + * + * @see av_rescale(), av_rescale_rnd(), av_rescale_q_rnd() + */ +int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const; + +/** + * Rescale a 64-bit integer by 2 rational numbers with specified rounding. + * + * The operation is mathematically equivalent to `a * bq / cq`. + * + * @see av_rescale(), av_rescale_rnd(), av_rescale_q() + */ +int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq, + enum AVRounding rnd) av_const; + +/** + * Compare two timestamps each in its own time base. + * + * @return One of the following values: + * - -1 if `ts_a` is before `ts_b` + * - 1 if `ts_a` is after `ts_b` + * - 0 if they represent the same position + * + * @warning + * The result of the function is undefined if one of the timestamps is outside + * the `int64_t` range when represented in the other's timebase. + */ +int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b); + +/** + * Compare the remainders of two integer operands divided by a common divisor. + * + * In other words, compare the least significant `log2(mod)` bits of integers + * `a` and `b`. + * + * @code{.c} + * av_compare_mod(0x11, 0x02, 0x10) < 0 // since 0x11 % 0x10 (0x1) < 0x02 % 0x10 (0x2) + * av_compare_mod(0x11, 0x02, 0x20) > 0 // since 0x11 % 0x20 (0x11) > 0x02 % 0x20 (0x02) + * @endcode + * + * @param a,b Operands + * @param mod Divisor; must be a power of 2 + * @return + * - a negative value if `a % mod < b % mod` + * - a positive value if `a % mod > b % mod` + * - zero if `a % mod == b % mod` + */ +int64_t av_compare_mod(uint64_t a, uint64_t b, uint64_t mod); + +/** + * Rescale a timestamp while preserving known durations. + * + * This function is designed to be called per audio packet to scale the input + * timestamp to a different time base. Compared to a simple av_rescale_q() + * call, this function is robust against possible inconsistent frame durations. + * + * The `last` parameter is a state variable that must be preserved for all + * subsequent calls for the same stream. For the first call, `*last` should be + * initialized to #AV_NOPTS_VALUE. + * + * @param[in] in_tb Input time base + * @param[in] in_ts Input timestamp + * @param[in] fs_tb Duration time base; typically this is finer-grained + * (greater) than `in_tb` and `out_tb` + * @param[in] duration Duration till the next call to this function (i.e. + * duration of the current packet/frame) + * @param[in,out] last Pointer to a timestamp expressed in terms of + * `fs_tb`, acting as a state variable + * @param[in] out_tb Output timebase + * @return Timestamp expressed in terms of `out_tb` + * + * @note In the context of this function, "duration" is in term of samples, not + * seconds. + */ +int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb); + +/** + * Add a value to a timestamp. + * + * This function guarantees that when the same value is repeatly added that + * no accumulation of rounding errors occurs. + * + * @param[in] ts Input timestamp + * @param[in] ts_tb Input timestamp time base + * @param[in] inc Value to be added + * @param[in] inc_tb Time base of `inc` + */ +int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t inc); + + +/** + * @} + */ + +#endif /* AVUTIL_MATHEMATICS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/md5.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/md5.h new file mode 100644 index 0000000..ca72ccb --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/md5.h
@@ -0,0 +1,98 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_md5 + * Public header for MD5 hash function implementation. + */ + +#ifndef AVUTIL_MD5_H +#define AVUTIL_MD5_H + +#include <stddef.h> +#include <stdint.h> + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_md5 MD5 + * @ingroup lavu_hash + * MD5 hash function implementation. + * + * @{ + */ + +extern const int av_md5_size; + +struct AVMD5; + +/** + * Allocate an AVMD5 context. + */ +struct AVMD5 *av_md5_alloc(void); + +/** + * Initialize MD5 hashing. + * + * @param ctx pointer to the function context (of size av_md5_size) + */ +void av_md5_init(struct AVMD5 *ctx); + +/** + * Update hash value. + * + * @param ctx hash function context + * @param src input data to update hash with + * @param len input data length + */ +#if FF_API_CRYPTO_SIZE_T +void av_md5_update(struct AVMD5 *ctx, const uint8_t *src, int len); +#else +void av_md5_update(struct AVMD5 *ctx, const uint8_t *src, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param ctx hash function context + * @param dst buffer where output digest value is stored + */ +void av_md5_final(struct AVMD5 *ctx, uint8_t *dst); + +/** + * Hash an array of data. + * + * @param dst The output buffer to write the digest into + * @param src The data to hash + * @param len The length of the data, in bytes + */ +#if FF_API_CRYPTO_SIZE_T +void av_md5_sum(uint8_t *dst, const uint8_t *src, const int len); +#else +void av_md5_sum(uint8_t *dst, const uint8_t *src, size_t len); +#endif + +/** + * @} + */ + +#endif /* AVUTIL_MD5_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mem.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mem.h new file mode 100644 index 0000000..7e0b12a --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mem.h
@@ -0,0 +1,700 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_mem + * Memory handling functions + */ + +#ifndef AVUTIL_MEM_H +#define AVUTIL_MEM_H + +#include <limits.h> +#include <stdint.h> + +#include "attributes.h" +#include "error.h" +#include "avutil.h" + +/** + * @addtogroup lavu_mem + * Utilities for manipulating memory. + * + * FFmpeg has several applications of memory that are not required of a typical + * program. For example, the computing-heavy components like video decoding and + * encoding can be sped up significantly through the use of aligned memory. + * + * However, for each of FFmpeg's applications of memory, there might not be a + * recognized or standardized API for that specific use. Memory alignment, for + * instance, varies wildly depending on operating systems, architectures, and + * compilers. Hence, this component of @ref libavutil is created to make + * dealing with memory consistently possible on all platforms. + * + * @{ + * + * @defgroup lavu_mem_macros Alignment Macros + * Helper macros for declaring aligned variables. + * @{ + */ + +/** + * @def DECLARE_ALIGNED(n,t,v) + * Declare a variable that is aligned in memory. + * + * @code{.c} + * DECLARE_ALIGNED(16, uint16_t, aligned_int) = 42; + * DECLARE_ALIGNED(32, uint8_t, aligned_array)[128]; + * + * // The default-alignment equivalent would be + * uint16_t aligned_int = 42; + * uint8_t aligned_array[128]; + * @endcode + * + * @param n Minimum alignment in bytes + * @param t Type of the variable (or array element) + * @param v Name of the variable + */ + +/** + * @def DECLARE_ASM_ALIGNED(n,t,v) + * Declare an aligned variable appropriate for use in inline assembly code. + * + * @code{.c} + * DECLARE_ASM_ALIGNED(16, uint64_t, pw_08) = UINT64_C(0x0008000800080008); + * @endcode + * + * @param n Minimum alignment in bytes + * @param t Type of the variable (or array element) + * @param v Name of the variable + */ + +/** + * @def DECLARE_ASM_CONST(n,t,v) + * Declare a static constant aligned variable appropriate for use in inline + * assembly code. + * + * @code{.c} + * DECLARE_ASM_CONST(16, uint64_t, pw_08) = UINT64_C(0x0008000800080008); + * @endcode + * + * @param n Minimum alignment in bytes + * @param t Type of the variable (or array element) + * @param v Name of the variable + */ + +#if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1110 || defined(__SUNPRO_C) + #define DECLARE_ALIGNED(n,t,v) t __attribute__ ((aligned (n))) v + #define DECLARE_ASM_ALIGNED(n,t,v) t __attribute__ ((aligned (n))) v + #define DECLARE_ASM_CONST(n,t,v) const t __attribute__ ((aligned (n))) v +#elif defined(__DJGPP__) + #define DECLARE_ALIGNED(n,t,v) t __attribute__ ((aligned (FFMIN(n, 16)))) v + #define DECLARE_ASM_ALIGNED(n,t,v) t av_used __attribute__ ((aligned (FFMIN(n, 16)))) v + #define DECLARE_ASM_CONST(n,t,v) static const t av_used __attribute__ ((aligned (FFMIN(n, 16)))) v +#elif defined(__GNUC__) || defined(__clang__) + #define DECLARE_ALIGNED(n,t,v) t __attribute__ ((aligned (n))) v + #define DECLARE_ASM_ALIGNED(n,t,v) t av_used __attribute__ ((aligned (n))) v + #define DECLARE_ASM_CONST(n,t,v) static const t av_used __attribute__ ((aligned (n))) v +#elif defined(_MSC_VER) + #define DECLARE_ALIGNED(n,t,v) __declspec(align(n)) t v + #define DECLARE_ASM_ALIGNED(n,t,v) __declspec(align(n)) t v + #define DECLARE_ASM_CONST(n,t,v) __declspec(align(n)) static const t v +#else + #define DECLARE_ALIGNED(n,t,v) t v + #define DECLARE_ASM_ALIGNED(n,t,v) t v + #define DECLARE_ASM_CONST(n,t,v) static const t v +#endif + +/** + * @} + */ + +/** + * @defgroup lavu_mem_attrs Function Attributes + * Function attributes applicable to memory handling functions. + * + * These function attributes can help compilers emit more useful warnings, or + * generate better code. + * @{ + */ + +/** + * @def av_malloc_attrib + * Function attribute denoting a malloc-like function. + * + * @see <a href="https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007bmalloc_007d-function-attribute-3251">Function attribute `malloc` in GCC's documentation</a> + */ + +#if AV_GCC_VERSION_AT_LEAST(3,1) + #define av_malloc_attrib __attribute__((__malloc__)) +#else + #define av_malloc_attrib +#endif + +/** + * @def av_alloc_size(...) + * Function attribute used on a function that allocates memory, whose size is + * given by the specified parameter(s). + * + * @code{.c} + * void *av_malloc(size_t size) av_alloc_size(1); + * void *av_calloc(size_t nmemb, size_t size) av_alloc_size(1, 2); + * @endcode + * + * @param ... One or two parameter indexes, separated by a comma + * + * @see <a href="https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007balloc_005fsize_007d-function-attribute-3220">Function attribute `alloc_size` in GCC's documentation</a> + */ + +#if AV_GCC_VERSION_AT_LEAST(4,3) + #define av_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) +#else + #define av_alloc_size(...) +#endif + +/** + * @} + */ + +/** + * @defgroup lavu_mem_funcs Heap Management + * Functions responsible for allocating, freeing, and copying memory. + * + * All memory allocation functions have a built-in upper limit of `INT_MAX` + * bytes. This may be changed with av_max_alloc(), although exercise extreme + * caution when doing so. + * + * @{ + */ + +/** + * Allocate a memory block with alignment suitable for all memory accesses + * (including vectors if available on the CPU). + * + * @param size Size in bytes for the memory block to be allocated + * @return Pointer to the allocated block, or `NULL` if the block cannot + * be allocated + * @see av_mallocz() + */ +void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1); + +/** + * Allocate a memory block with alignment suitable for all memory accesses + * (including vectors if available on the CPU) and zero all the bytes of the + * block. + * + * @param size Size in bytes for the memory block to be allocated + * @return Pointer to the allocated block, or `NULL` if it cannot be allocated + * @see av_malloc() + */ +void *av_mallocz(size_t size) av_malloc_attrib av_alloc_size(1); + +/** + * Allocate a memory block for an array with av_malloc(). + * + * The allocated memory will have size `size * nmemb` bytes. + * + * @param nmemb Number of element + * @param size Size of a single element + * @return Pointer to the allocated block, or `NULL` if the block cannot + * be allocated + * @see av_malloc() + */ +av_alloc_size(1, 2) void *av_malloc_array(size_t nmemb, size_t size); + +/** + * Allocate a memory block for an array with av_mallocz(). + * + * The allocated memory will have size `size * nmemb` bytes. + * + * @param nmemb Number of elements + * @param size Size of the single element + * @return Pointer to the allocated block, or `NULL` if the block cannot + * be allocated + * + * @see av_mallocz() + * @see av_malloc_array() + */ +av_alloc_size(1, 2) void *av_mallocz_array(size_t nmemb, size_t size); + +/** + * Non-inlined equivalent of av_mallocz_array(). + * + * Created for symmetry with the calloc() C function. + */ +void *av_calloc(size_t nmemb, size_t size) av_malloc_attrib; + +/** + * Allocate, reallocate, or free a block of memory. + * + * If `ptr` is `NULL` and `size` > 0, allocate a new block. If `size` is + * zero, free the memory block pointed to by `ptr`. Otherwise, expand or + * shrink that block of memory according to `size`. + * + * @param ptr Pointer to a memory block already allocated with + * av_realloc() or `NULL` + * @param size Size in bytes of the memory block to be allocated or + * reallocated + * + * @return Pointer to a newly-reallocated block or `NULL` if the block + * cannot be reallocated or the function is used to free the memory block + * + * @warning Unlike av_malloc(), the returned pointer is not guaranteed to be + * correctly aligned. + * @see av_fast_realloc() + * @see av_reallocp() + */ +void *av_realloc(void *ptr, size_t size) av_alloc_size(2); + +/** + * Allocate, reallocate, or free a block of memory through a pointer to a + * pointer. + * + * If `*ptr` is `NULL` and `size` > 0, allocate a new block. If `size` is + * zero, free the memory block pointed to by `*ptr`. Otherwise, expand or + * shrink that block of memory according to `size`. + * + * @param[in,out] ptr Pointer to a pointer to a memory block already allocated + * with av_realloc(), or a pointer to `NULL`. The pointer + * is updated on success, or freed on failure. + * @param[in] size Size in bytes for the memory block to be allocated or + * reallocated + * + * @return Zero on success, an AVERROR error code on failure + * + * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be + * correctly aligned. + */ +av_warn_unused_result +int av_reallocp(void *ptr, size_t size); + +/** + * Allocate, reallocate, or free a block of memory. + * + * This function does the same thing as av_realloc(), except: + * - It takes two size arguments and allocates `nelem * elsize` bytes, + * after checking the result of the multiplication for integer overflow. + * - It frees the input block in case of failure, thus avoiding the memory + * leak with the classic + * @code{.c} + * buf = realloc(buf); + * if (!buf) + * return -1; + * @endcode + * pattern. + */ +void *av_realloc_f(void *ptr, size_t nelem, size_t elsize); + +/** + * Allocate, reallocate, or free an array. + * + * If `ptr` is `NULL` and `nmemb` > 0, allocate a new block. If + * `nmemb` is zero, free the memory block pointed to by `ptr`. + * + * @param ptr Pointer to a memory block already allocated with + * av_realloc() or `NULL` + * @param nmemb Number of elements in the array + * @param size Size of the single element of the array + * + * @return Pointer to a newly-reallocated block or NULL if the block + * cannot be reallocated or the function is used to free the memory block + * + * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be + * correctly aligned. + * @see av_reallocp_array() + */ +av_alloc_size(2, 3) void *av_realloc_array(void *ptr, size_t nmemb, size_t size); + +/** + * Allocate, reallocate, or free an array through a pointer to a pointer. + * + * If `*ptr` is `NULL` and `nmemb` > 0, allocate a new block. If `nmemb` is + * zero, free the memory block pointed to by `*ptr`. + * + * @param[in,out] ptr Pointer to a pointer to a memory block already + * allocated with av_realloc(), or a pointer to `NULL`. + * The pointer is updated on success, or freed on failure. + * @param[in] nmemb Number of elements + * @param[in] size Size of the single element + * + * @return Zero on success, an AVERROR error code on failure + * + * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be + * correctly aligned. + */ +av_alloc_size(2, 3) int av_reallocp_array(void *ptr, size_t nmemb, size_t size); + +/** + * Reallocate the given buffer if it is not large enough, otherwise do nothing. + * + * If the given buffer is `NULL`, then a new uninitialized buffer is allocated. + * + * If the given buffer is not large enough, and reallocation fails, `NULL` is + * returned and `*size` is set to 0, but the original buffer is not changed or + * freed. + * + * A typical use pattern follows: + * + * @code{.c} + * uint8_t *buf = ...; + * uint8_t *new_buf = av_fast_realloc(buf, ¤t_size, size_needed); + * if (!new_buf) { + * // Allocation failed; clean up original buffer + * av_freep(&buf); + * return AVERROR(ENOMEM); + * } + * @endcode + * + * @param[in,out] ptr Already allocated buffer, or `NULL` + * @param[in,out] size Pointer to current size of buffer `ptr`. `*size` is + * changed to `min_size` in case of success or 0 in + * case of failure + * @param[in] min_size New size of buffer `ptr` + * @return `ptr` if the buffer is large enough, a pointer to newly reallocated + * buffer if the buffer was not large enough, or `NULL` in case of + * error + * @see av_realloc() + * @see av_fast_malloc() + */ +void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size); + +/** + * Allocate a buffer, reusing the given one if large enough. + * + * Contrary to av_fast_realloc(), the current buffer contents might not be + * preserved and on error the old buffer is freed, thus no special handling to + * avoid memleaks is necessary. + * + * `*ptr` is allowed to be `NULL`, in which case allocation always happens if + * `size_needed` is greater than 0. + * + * @code{.c} + * uint8_t *buf = ...; + * av_fast_malloc(&buf, ¤t_size, size_needed); + * if (!buf) { + * // Allocation failed; buf already freed + * return AVERROR(ENOMEM); + * } + * @endcode + * + * @param[in,out] ptr Pointer to pointer to an already allocated buffer. + * `*ptr` will be overwritten with pointer to new + * buffer on success or `NULL` on failure + * @param[in,out] size Pointer to current size of buffer `*ptr`. `*size` is + * changed to `min_size` in case of success or 0 in + * case of failure + * @param[in] min_size New size of buffer `*ptr` + * @see av_realloc() + * @see av_fast_mallocz() + */ +void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size); + +/** + * Allocate and clear a buffer, reusing the given one if large enough. + * + * Like av_fast_malloc(), but all newly allocated space is initially cleared. + * Reused buffer is not cleared. + * + * `*ptr` is allowed to be `NULL`, in which case allocation always happens if + * `size_needed` is greater than 0. + * + * @param[in,out] ptr Pointer to pointer to an already allocated buffer. + * `*ptr` will be overwritten with pointer to new + * buffer on success or `NULL` on failure + * @param[in,out] size Pointer to current size of buffer `*ptr`. `*size` is + * changed to `min_size` in case of success or 0 in + * case of failure + * @param[in] min_size New size of buffer `*ptr` + * @see av_fast_malloc() + */ +void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size); + +/** + * Free a memory block which has been allocated with a function of av_malloc() + * or av_realloc() family. + * + * @param ptr Pointer to the memory block which should be freed. + * + * @note `ptr = NULL` is explicitly allowed. + * @note It is recommended that you use av_freep() instead, to prevent leaving + * behind dangling pointers. + * @see av_freep() + */ +void av_free(void *ptr); + +/** + * Free a memory block which has been allocated with a function of av_malloc() + * or av_realloc() family, and set the pointer pointing to it to `NULL`. + * + * @code{.c} + * uint8_t *buf = av_malloc(16); + * av_free(buf); + * // buf now contains a dangling pointer to freed memory, and accidental + * // dereference of buf will result in a use-after-free, which may be a + * // security risk. + * + * uint8_t *buf = av_malloc(16); + * av_freep(&buf); + * // buf is now NULL, and accidental dereference will only result in a + * // NULL-pointer dereference. + * @endcode + * + * @param ptr Pointer to the pointer to the memory block which should be freed + * @note `*ptr = NULL` is safe and leads to no action. + * @see av_free() + */ +void av_freep(void *ptr); + +/** + * Duplicate a string. + * + * @param s String to be duplicated + * @return Pointer to a newly-allocated string containing a + * copy of `s` or `NULL` if the string cannot be allocated + * @see av_strndup() + */ +char *av_strdup(const char *s) av_malloc_attrib; + +/** + * Duplicate a substring of a string. + * + * @param s String to be duplicated + * @param len Maximum length of the resulting string (not counting the + * terminating byte) + * @return Pointer to a newly-allocated string containing a + * substring of `s` or `NULL` if the string cannot be allocated + */ +char *av_strndup(const char *s, size_t len) av_malloc_attrib; + +/** + * Duplicate a buffer with av_malloc(). + * + * @param p Buffer to be duplicated + * @param size Size in bytes of the buffer copied + * @return Pointer to a newly allocated buffer containing a + * copy of `p` or `NULL` if the buffer cannot be allocated + */ +void *av_memdup(const void *p, size_t size); + +/** + * Overlapping memcpy() implementation. + * + * @param dst Destination buffer + * @param back Number of bytes back to start copying (i.e. the initial size of + * the overlapping window); must be > 0 + * @param cnt Number of bytes to copy; must be >= 0 + * + * @note `cnt > back` is valid, this will copy the bytes we just copied, + * thus creating a repeating pattern with a period length of `back`. + */ +void av_memcpy_backptr(uint8_t *dst, int back, int cnt); + +/** + * @} + */ + +/** + * @defgroup lavu_mem_dynarray Dynamic Array + * + * Utilities to make an array grow when needed. + * + * Sometimes, the programmer would want to have an array that can grow when + * needed. The libavutil dynamic array utilities fill that need. + * + * libavutil supports two systems of appending elements onto a dynamically + * allocated array, the first one storing the pointer to the value in the + * array, and the second storing the value directly. In both systems, the + * caller is responsible for maintaining a variable containing the length of + * the array, as well as freeing of the array after use. + * + * The first system stores pointers to values in a block of dynamically + * allocated memory. Since only pointers are stored, the function does not need + * to know the size of the type. Both av_dynarray_add() and + * av_dynarray_add_nofree() implement this system. + * + * @code + * type **array = NULL; //< an array of pointers to values + * int nb = 0; //< a variable to keep track of the length of the array + * + * type to_be_added = ...; + * type to_be_added2 = ...; + * + * av_dynarray_add(&array, &nb, &to_be_added); + * if (nb == 0) + * return AVERROR(ENOMEM); + * + * av_dynarray_add(&array, &nb, &to_be_added2); + * if (nb == 0) + * return AVERROR(ENOMEM); + * + * // Now: + * // nb == 2 + * // &to_be_added == array[0] + * // &to_be_added2 == array[1] + * + * av_freep(&array); + * @endcode + * + * The second system stores the value directly in a block of memory. As a + * result, the function has to know the size of the type. av_dynarray2_add() + * implements this mechanism. + * + * @code + * type *array = NULL; //< an array of values + * int nb = 0; //< a variable to keep track of the length of the array + * + * type to_be_added = ...; + * type to_be_added2 = ...; + * + * type *addr = av_dynarray2_add((void **)&array, &nb, sizeof(*array), NULL); + * if (!addr) + * return AVERROR(ENOMEM); + * memcpy(addr, &to_be_added, sizeof(to_be_added)); + * + * // Shortcut of the above. + * type *addr = av_dynarray2_add((void **)&array, &nb, sizeof(*array), + * (const void *)&to_be_added2); + * if (!addr) + * return AVERROR(ENOMEM); + * + * // Now: + * // nb == 2 + * // to_be_added == array[0] + * // to_be_added2 == array[1] + * + * av_freep(&array); + * @endcode + * + * @{ + */ + +/** + * Add the pointer to an element to a dynamic array. + * + * The array to grow is supposed to be an array of pointers to + * structures, and the element to add must be a pointer to an already + * allocated structure. + * + * The array is reallocated when its size reaches powers of 2. + * Therefore, the amortized cost of adding an element is constant. + * + * In case of success, the pointer to the array is updated in order to + * point to the new grown array, and the number pointed to by `nb_ptr` + * is incremented. + * In case of failure, the array is freed, `*tab_ptr` is set to `NULL` and + * `*nb_ptr` is set to 0. + * + * @param[in,out] tab_ptr Pointer to the array to grow + * @param[in,out] nb_ptr Pointer to the number of elements in the array + * @param[in] elem Element to add + * @see av_dynarray_add_nofree(), av_dynarray2_add() + */ +void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem); + +/** + * Add an element to a dynamic array. + * + * Function has the same functionality as av_dynarray_add(), + * but it doesn't free memory on fails. It returns error code + * instead and leave current buffer untouched. + * + * @return >=0 on success, negative otherwise + * @see av_dynarray_add(), av_dynarray2_add() + */ +av_warn_unused_result +int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem); + +/** + * Add an element of size `elem_size` to a dynamic array. + * + * The array is reallocated when its number of elements reaches powers of 2. + * Therefore, the amortized cost of adding an element is constant. + * + * In case of success, the pointer to the array is updated in order to + * point to the new grown array, and the number pointed to by `nb_ptr` + * is incremented. + * In case of failure, the array is freed, `*tab_ptr` is set to `NULL` and + * `*nb_ptr` is set to 0. + * + * @param[in,out] tab_ptr Pointer to the array to grow + * @param[in,out] nb_ptr Pointer to the number of elements in the array + * @param[in] elem_size Size in bytes of an element in the array + * @param[in] elem_data Pointer to the data of the element to add. If + * `NULL`, the space of the newly added element is + * allocated but left uninitialized. + * + * @return Pointer to the data of the element to copy in the newly allocated + * space + * @see av_dynarray_add(), av_dynarray_add_nofree() + */ +void *av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size, + const uint8_t *elem_data); + +/** + * @} + */ + +/** + * @defgroup lavu_mem_misc Miscellaneous Functions + * + * Other functions related to memory allocation. + * + * @{ + */ + +/** + * Multiply two `size_t` values checking for overflow. + * + * @param[in] a,b Operands of multiplication + * @param[out] r Pointer to the result of the operation + * @return 0 on success, AVERROR(EINVAL) on overflow + */ +static inline int av_size_mult(size_t a, size_t b, size_t *r) +{ + size_t t = a * b; + /* Hack inspired from glibc: don't try the division if nelem and elsize + * are both less than sqrt(SIZE_MAX). */ + if ((a | b) >= ((size_t)1 << (sizeof(size_t) * 4)) && a && t / a != b) + return AVERROR(EINVAL); + *r = t; + return 0; +} + +/** + * Set the maximum size that may be allocated in one block. + * + * The value specified with this function is effective for all libavutil's @ref + * lavu_mem_funcs "heap management functions." + * + * By default, the max value is defined as `INT_MAX`. + * + * @param max Value to be set as the new maximum size + * + * @warning Exercise extreme caution when using this function. Don't touch + * this if you do not understand the full consequence of doing so. + */ +void av_max_alloc(size_t max); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_MEM_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/motion_vector.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/motion_vector.h new file mode 100644 index 0000000..ec29556 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/motion_vector.h
@@ -0,0 +1,57 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_MOTION_VECTOR_H +#define AVUTIL_MOTION_VECTOR_H + +#include <stdint.h> + +typedef struct AVMotionVector { + /** + * Where the current macroblock comes from; negative value when it comes + * from the past, positive value when it comes from the future. + * XXX: set exact relative ref frame reference instead of a +/- 1 "direction". + */ + int32_t source; + /** + * Width and height of the block. + */ + uint8_t w, h; + /** + * Absolute source position. Can be outside the frame area. + */ + int16_t src_x, src_y; + /** + * Absolute destination position. Can be outside the frame area. + */ + int16_t dst_x, dst_y; + /** + * Extra flag information. + * Currently unused. + */ + uint64_t flags; + /** + * Motion vector + * src_x = dst_x + motion_x / motion_scale + * src_y = dst_y + motion_y / motion_scale + */ + int32_t motion_x, motion_y; + uint16_t motion_scale; +} AVMotionVector; + +#endif /* AVUTIL_MOTION_VECTOR_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/murmur3.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/murmur3.h new file mode 100644 index 0000000..1b09175 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/murmur3.h
@@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013 Reimar Döffinger <Reimar.Doeffinger@gmx.de> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_murmur3 + * Public header for MurmurHash3 hash function implementation. + */ + +#ifndef AVUTIL_MURMUR3_H +#define AVUTIL_MURMUR3_H + +#include <stdint.h> + +#include "version.h" + +/** + * @defgroup lavu_murmur3 Murmur3 + * @ingroup lavu_hash + * MurmurHash3 hash function implementation. + * + * MurmurHash3 is a non-cryptographic hash function, of which three + * incompatible versions were created by its inventor Austin Appleby: + * + * - 32-bit output + * - 128-bit output for 32-bit platforms + * - 128-bit output for 64-bit platforms + * + * FFmpeg only implements the last variant: 128-bit output designed for 64-bit + * platforms. Even though the hash function was designed for 64-bit platforms, + * the function in reality works on 32-bit systems too, only with reduced + * performance. + * + * @anchor lavu_murmur3_seedinfo + * By design, MurmurHash3 requires a seed to operate. In response to this, + * libavutil provides two functions for hash initiation, one that requires a + * seed (av_murmur3_init_seeded()) and one that uses a fixed arbitrary integer + * as the seed, and therefore does not (av_murmur3_init()). + * + * To make hashes comparable, you should provide the same seed for all calls to + * this hash function -- if you are supplying one yourself, that is. + * + * @{ + */ + +/** + * Allocate an AVMurMur3 hash context. + * + * @return Uninitialized hash context or `NULL` in case of error + */ +struct AVMurMur3 *av_murmur3_alloc(void); + +/** + * Initialize or reinitialize an AVMurMur3 hash context with a seed. + * + * @param[out] c Hash context + * @param[in] seed Random seed + * + * @see av_murmur3_init() + * @see @ref lavu_murmur3_seedinfo "Detailed description" on a discussion of + * seeds for MurmurHash3. + */ +void av_murmur3_init_seeded(struct AVMurMur3 *c, uint64_t seed); + +/** + * Initialize or reinitialize an AVMurMur3 hash context. + * + * Equivalent to av_murmur3_init_seeded() with a built-in seed. + * + * @param[out] c Hash context + * + * @see av_murmur3_init_seeded() + * @see @ref lavu_murmur3_seedinfo "Detailed description" on a discussion of + * seeds for MurmurHash3. + */ +void av_murmur3_init(struct AVMurMur3 *c); + +/** + * Update hash context with new data. + * + * @param[out] c Hash context + * @param[in] src Input data to update hash with + * @param[in] len Number of bytes to read from `src` + */ +#if FF_API_CRYPTO_SIZE_T +void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, int len); +#else +void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param[in,out] c Hash context + * @param[out] dst Buffer where output digest value is stored + */ +void av_murmur3_final(struct AVMurMur3 *c, uint8_t dst[16]); + +/** + * @} + */ + +#endif /* AVUTIL_MURMUR3_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/opt.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/opt.h new file mode 100644 index 0000000..39f4a8d --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/opt.h
@@ -0,0 +1,865 @@ +/* + * AVOptions + * copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_OPT_H +#define AVUTIL_OPT_H + +/** + * @file + * AVOptions + */ + +#include "rational.h" +#include "avutil.h" +#include "dict.h" +#include "log.h" +#include "pixfmt.h" +#include "samplefmt.h" +#include "version.h" + +/** + * @defgroup avoptions AVOptions + * @ingroup lavu_data + * @{ + * AVOptions provide a generic system to declare options on arbitrary structs + * ("objects"). An option can have a help text, a type and a range of possible + * values. Options may then be enumerated, read and written to. + * + * @section avoptions_implement Implementing AVOptions + * This section describes how to add AVOptions capabilities to a struct. + * + * All AVOptions-related information is stored in an AVClass. Therefore + * the first member of the struct should be a pointer to an AVClass describing it. + * The option field of the AVClass must be set to a NULL-terminated static array + * of AVOptions. Each AVOption must have a non-empty name, a type, a default + * value and for number-type AVOptions also a range of allowed values. It must + * also declare an offset in bytes from the start of the struct, where the field + * associated with this AVOption is located. Other fields in the AVOption struct + * should also be set when applicable, but are not required. + * + * The following example illustrates an AVOptions-enabled struct: + * @code + * typedef struct test_struct { + * const AVClass *class; + * int int_opt; + * char *str_opt; + * uint8_t *bin_opt; + * int bin_len; + * } test_struct; + * + * static const AVOption test_options[] = { + * { "test_int", "This is a test option of int type.", offsetof(test_struct, int_opt), + * AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX }, + * { "test_str", "This is a test option of string type.", offsetof(test_struct, str_opt), + * AV_OPT_TYPE_STRING }, + * { "test_bin", "This is a test option of binary type.", offsetof(test_struct, bin_opt), + * AV_OPT_TYPE_BINARY }, + * { NULL }, + * }; + * + * static const AVClass test_class = { + * .class_name = "test class", + * .item_name = av_default_item_name, + * .option = test_options, + * .version = LIBAVUTIL_VERSION_INT, + * }; + * @endcode + * + * Next, when allocating your struct, you must ensure that the AVClass pointer + * is set to the correct value. Then, av_opt_set_defaults() can be called to + * initialize defaults. After that the struct is ready to be used with the + * AVOptions API. + * + * When cleaning up, you may use the av_opt_free() function to automatically + * free all the allocated string and binary options. + * + * Continuing with the above example: + * + * @code + * test_struct *alloc_test_struct(void) + * { + * test_struct *ret = av_mallocz(sizeof(*ret)); + * ret->class = &test_class; + * av_opt_set_defaults(ret); + * return ret; + * } + * void free_test_struct(test_struct **foo) + * { + * av_opt_free(*foo); + * av_freep(foo); + * } + * @endcode + * + * @subsection avoptions_implement_nesting Nesting + * It may happen that an AVOptions-enabled struct contains another + * AVOptions-enabled struct as a member (e.g. AVCodecContext in + * libavcodec exports generic options, while its priv_data field exports + * codec-specific options). In such a case, it is possible to set up the + * parent struct to export a child's options. To do that, simply + * implement AVClass.child_next() and AVClass.child_class_next() in the + * parent struct's AVClass. + * Assuming that the test_struct from above now also contains a + * child_struct field: + * + * @code + * typedef struct child_struct { + * AVClass *class; + * int flags_opt; + * } child_struct; + * static const AVOption child_opts[] = { + * { "test_flags", "This is a test option of flags type.", + * offsetof(child_struct, flags_opt), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT_MIN, INT_MAX }, + * { NULL }, + * }; + * static const AVClass child_class = { + * .class_name = "child class", + * .item_name = av_default_item_name, + * .option = child_opts, + * .version = LIBAVUTIL_VERSION_INT, + * }; + * + * void *child_next(void *obj, void *prev) + * { + * test_struct *t = obj; + * if (!prev && t->child_struct) + * return t->child_struct; + * return NULL + * } + * const AVClass child_class_next(const AVClass *prev) + * { + * return prev ? NULL : &child_class; + * } + * @endcode + * Putting child_next() and child_class_next() as defined above into + * test_class will now make child_struct's options accessible through + * test_struct (again, proper setup as described above needs to be done on + * child_struct right after it is created). + * + * From the above example it might not be clear why both child_next() + * and child_class_next() are needed. The distinction is that child_next() + * iterates over actually existing objects, while child_class_next() + * iterates over all possible child classes. E.g. if an AVCodecContext + * was initialized to use a codec which has private options, then its + * child_next() will return AVCodecContext.priv_data and finish + * iterating. OTOH child_class_next() on AVCodecContext.av_class will + * iterate over all available codecs with private options. + * + * @subsection avoptions_implement_named_constants Named constants + * It is possible to create named constants for options. Simply set the unit + * field of the option the constants should apply to a string and + * create the constants themselves as options of type AV_OPT_TYPE_CONST + * with their unit field set to the same string. + * Their default_val field should contain the value of the named + * constant. + * For example, to add some named constants for the test_flags option + * above, put the following into the child_opts array: + * @code + * { "test_flags", "This is a test option of flags type.", + * offsetof(child_struct, flags_opt), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT_MIN, INT_MAX, "test_unit" }, + * { "flag1", "This is a flag with value 16", 0, AV_OPT_TYPE_CONST, { .i64 = 16 }, 0, 0, "test_unit" }, + * @endcode + * + * @section avoptions_use Using AVOptions + * This section deals with accessing options in an AVOptions-enabled struct. + * Such structs in FFmpeg are e.g. AVCodecContext in libavcodec or + * AVFormatContext in libavformat. + * + * @subsection avoptions_use_examine Examining AVOptions + * The basic functions for examining options are av_opt_next(), which iterates + * over all options defined for one object, and av_opt_find(), which searches + * for an option with the given name. + * + * The situation is more complicated with nesting. An AVOptions-enabled struct + * may have AVOptions-enabled children. Passing the AV_OPT_SEARCH_CHILDREN flag + * to av_opt_find() will make the function search children recursively. + * + * For enumerating there are basically two cases. The first is when you want to + * get all options that may potentially exist on the struct and its children + * (e.g. when constructing documentation). In that case you should call + * av_opt_child_class_next() recursively on the parent struct's AVClass. The + * second case is when you have an already initialized struct with all its + * children and you want to get all options that can be actually written or read + * from it. In that case you should call av_opt_child_next() recursively (and + * av_opt_next() on each result). + * + * @subsection avoptions_use_get_set Reading and writing AVOptions + * When setting options, you often have a string read directly from the + * user. In such a case, simply passing it to av_opt_set() is enough. For + * non-string type options, av_opt_set() will parse the string according to the + * option type. + * + * Similarly av_opt_get() will read any option type and convert it to a string + * which will be returned. Do not forget that the string is allocated, so you + * have to free it with av_free(). + * + * In some cases it may be more convenient to put all options into an + * AVDictionary and call av_opt_set_dict() on it. A specific case of this + * are the format/codec open functions in lavf/lavc which take a dictionary + * filled with option as a parameter. This makes it possible to set some options + * that cannot be set otherwise, since e.g. the input file format is not known + * before the file is actually opened. + */ + +enum AVOptionType{ + AV_OPT_TYPE_FLAGS, + AV_OPT_TYPE_INT, + AV_OPT_TYPE_INT64, + AV_OPT_TYPE_DOUBLE, + AV_OPT_TYPE_FLOAT, + AV_OPT_TYPE_STRING, + AV_OPT_TYPE_RATIONAL, + AV_OPT_TYPE_BINARY, ///< offset must point to a pointer immediately followed by an int for the length + AV_OPT_TYPE_DICT, + AV_OPT_TYPE_UINT64, + AV_OPT_TYPE_CONST, + AV_OPT_TYPE_IMAGE_SIZE, ///< offset must point to two consecutive integers + AV_OPT_TYPE_PIXEL_FMT, + AV_OPT_TYPE_SAMPLE_FMT, + AV_OPT_TYPE_VIDEO_RATE, ///< offset must point to AVRational + AV_OPT_TYPE_DURATION, + AV_OPT_TYPE_COLOR, + AV_OPT_TYPE_CHANNEL_LAYOUT, + AV_OPT_TYPE_BOOL, +}; + +/** + * AVOption + */ +typedef struct AVOption { + const char *name; + + /** + * short English help text + * @todo What about other languages? + */ + const char *help; + + /** + * The offset relative to the context structure where the option + * value is stored. It should be 0 for named constants. + */ + int offset; + enum AVOptionType type; + + /** + * the default value for scalar options + */ + union { + int64_t i64; + double dbl; + const char *str; + /* TODO those are unused now */ + AVRational q; + } default_val; + double min; ///< minimum valid value for the option + double max; ///< maximum valid value for the option + + int flags; +#define AV_OPT_FLAG_ENCODING_PARAM 1 ///< a generic parameter which can be set by the user for muxing or encoding +#define AV_OPT_FLAG_DECODING_PARAM 2 ///< a generic parameter which can be set by the user for demuxing or decoding +#define AV_OPT_FLAG_AUDIO_PARAM 8 +#define AV_OPT_FLAG_VIDEO_PARAM 16 +#define AV_OPT_FLAG_SUBTITLE_PARAM 32 +/** + * The option is intended for exporting values to the caller. + */ +#define AV_OPT_FLAG_EXPORT 64 +/** + * The option may not be set through the AVOptions API, only read. + * This flag only makes sense when AV_OPT_FLAG_EXPORT is also set. + */ +#define AV_OPT_FLAG_READONLY 128 +#define AV_OPT_FLAG_BSF_PARAM (1<<8) ///< a generic parameter which can be set by the user for bit stream filtering +#define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering +#define AV_OPT_FLAG_DEPRECATED (1<<17) ///< set if option is deprecated, users should refer to AVOption.help text for more information +//FIXME think about enc-audio, ... style flags + + /** + * The logical unit to which the option belongs. Non-constant + * options and corresponding named constants share the same + * unit. May be NULL. + */ + const char *unit; +} AVOption; + +/** + * A single allowed range of values, or a single allowed value. + */ +typedef struct AVOptionRange { + const char *str; + /** + * Value range. + * For string ranges this represents the min/max length. + * For dimensions this represents the min/max pixel count or width/height in multi-component case. + */ + double value_min, value_max; + /** + * Value's component range. + * For string this represents the unicode range for chars, 0-127 limits to ASCII. + */ + double component_min, component_max; + /** + * Range flag. + * If set to 1 the struct encodes a range, if set to 0 a single value. + */ + int is_range; +} AVOptionRange; + +/** + * List of AVOptionRange structs. + */ +typedef struct AVOptionRanges { + /** + * Array of option ranges. + * + * Most of option types use just one component. + * Following describes multi-component option types: + * + * AV_OPT_TYPE_IMAGE_SIZE: + * component index 0: range of pixel count (width * height). + * component index 1: range of width. + * component index 2: range of height. + * + * @note To obtain multi-component version of this structure, user must + * provide AV_OPT_MULTI_COMPONENT_RANGE to av_opt_query_ranges or + * av_opt_query_ranges_default function. + * + * Multi-component range can be read as in following example: + * + * @code + * int range_index, component_index; + * AVOptionRanges *ranges; + * AVOptionRange *range[3]; //may require more than 3 in the future. + * av_opt_query_ranges(&ranges, obj, key, AV_OPT_MULTI_COMPONENT_RANGE); + * for (range_index = 0; range_index < ranges->nb_ranges; range_index++) { + * for (component_index = 0; component_index < ranges->nb_components; component_index++) + * range[component_index] = ranges->range[ranges->nb_ranges * component_index + range_index]; + * //do something with range here. + * } + * av_opt_freep_ranges(&ranges); + * @endcode + */ + AVOptionRange **range; + /** + * Number of ranges per component. + */ + int nb_ranges; + /** + * Number of componentes. + */ + int nb_components; +} AVOptionRanges; + +/** + * Show the obj options. + * + * @param req_flags requested flags for the options to show. Show only the + * options for which it is opt->flags & req_flags. + * @param rej_flags rejected flags for the options to show. Show only the + * options for which it is !(opt->flags & req_flags). + * @param av_log_obj log context to use for showing the options + */ +int av_opt_show2(void *obj, void *av_log_obj, int req_flags, int rej_flags); + +/** + * Set the values of all AVOption fields to their default values. + * + * @param s an AVOption-enabled struct (its first member must be a pointer to AVClass) + */ +void av_opt_set_defaults(void *s); + +/** + * Set the values of all AVOption fields to their default values. Only these + * AVOption fields for which (opt->flags & mask) == flags will have their + * default applied to s. + * + * @param s an AVOption-enabled struct (its first member must be a pointer to AVClass) + * @param mask combination of AV_OPT_FLAG_* + * @param flags combination of AV_OPT_FLAG_* + */ +void av_opt_set_defaults2(void *s, int mask, int flags); + +/** + * Parse the key/value pairs list in opts. For each key/value pair + * found, stores the value in the field in ctx that is named like the + * key. ctx must be an AVClass context, storing is done using + * AVOptions. + * + * @param opts options string to parse, may be NULL + * @param key_val_sep a 0-terminated list of characters used to + * separate key from value + * @param pairs_sep a 0-terminated list of characters used to separate + * two pairs from each other + * @return the number of successfully set key/value pairs, or a negative + * value corresponding to an AVERROR code in case of error: + * AVERROR(EINVAL) if opts cannot be parsed, + * the error code issued by av_opt_set() if a key/value pair + * cannot be set + */ +int av_set_options_string(void *ctx, const char *opts, + const char *key_val_sep, const char *pairs_sep); + +/** + * Parse the key-value pairs list in opts. For each key=value pair found, + * set the value of the corresponding option in ctx. + * + * @param ctx the AVClass object to set options on + * @param opts the options string, key-value pairs separated by a + * delimiter + * @param shorthand a NULL-terminated array of options names for shorthand + * notation: if the first field in opts has no key part, + * the key is taken from the first element of shorthand; + * then again for the second, etc., until either opts is + * finished, shorthand is finished or a named option is + * found; after that, all options must be named + * @param key_val_sep a 0-terminated list of characters used to separate + * key from value, for example '=' + * @param pairs_sep a 0-terminated list of characters used to separate + * two pairs from each other, for example ':' or ',' + * @return the number of successfully set key=value pairs, or a negative + * value corresponding to an AVERROR code in case of error: + * AVERROR(EINVAL) if opts cannot be parsed, + * the error code issued by av_set_string3() if a key/value pair + * cannot be set + * + * Options names must use only the following characters: a-z A-Z 0-9 - . / _ + * Separators must use characters distinct from option names and from each + * other. + */ +int av_opt_set_from_string(void *ctx, const char *opts, + const char *const *shorthand, + const char *key_val_sep, const char *pairs_sep); +/** + * Free all allocated objects in obj. + */ +void av_opt_free(void *obj); + +/** + * Check whether a particular flag is set in a flags field. + * + * @param field_name the name of the flag field option + * @param flag_name the name of the flag to check + * @return non-zero if the flag is set, zero if the flag isn't set, + * isn't of the right type, or the flags field doesn't exist. + */ +int av_opt_flag_is_set(void *obj, const char *field_name, const char *flag_name); + +/** + * Set all the options from a given dictionary on an object. + * + * @param obj a struct whose first element is a pointer to AVClass + * @param options options to process. This dictionary will be freed and replaced + * by a new one containing all options not found in obj. + * Of course this new dictionary needs to be freed by caller + * with av_dict_free(). + * + * @return 0 on success, a negative AVERROR if some option was found in obj, + * but could not be set. + * + * @see av_dict_copy() + */ +int av_opt_set_dict(void *obj, struct AVDictionary **options); + + +/** + * Set all the options from a given dictionary on an object. + * + * @param obj a struct whose first element is a pointer to AVClass + * @param options options to process. This dictionary will be freed and replaced + * by a new one containing all options not found in obj. + * Of course this new dictionary needs to be freed by caller + * with av_dict_free(). + * @param search_flags A combination of AV_OPT_SEARCH_*. + * + * @return 0 on success, a negative AVERROR if some option was found in obj, + * but could not be set. + * + * @see av_dict_copy() + */ +int av_opt_set_dict2(void *obj, struct AVDictionary **options, int search_flags); + +/** + * Extract a key-value pair from the beginning of a string. + * + * @param ropts pointer to the options string, will be updated to + * point to the rest of the string (one of the pairs_sep + * or the final NUL) + * @param key_val_sep a 0-terminated list of characters used to separate + * key from value, for example '=' + * @param pairs_sep a 0-terminated list of characters used to separate + * two pairs from each other, for example ':' or ',' + * @param flags flags; see the AV_OPT_FLAG_* values below + * @param rkey parsed key; must be freed using av_free() + * @param rval parsed value; must be freed using av_free() + * + * @return >=0 for success, or a negative value corresponding to an + * AVERROR code in case of error; in particular: + * AVERROR(EINVAL) if no key is present + * + */ +int av_opt_get_key_value(const char **ropts, + const char *key_val_sep, const char *pairs_sep, + unsigned flags, + char **rkey, char **rval); + +enum { + + /** + * Accept to parse a value without a key; the key will then be returned + * as NULL. + */ + AV_OPT_FLAG_IMPLICIT_KEY = 1, +}; + +/** + * @defgroup opt_eval_funcs Evaluating option strings + * @{ + * This group of functions can be used to evaluate option strings + * and get numbers out of them. They do the same thing as av_opt_set(), + * except the result is written into the caller-supplied pointer. + * + * @param obj a struct whose first element is a pointer to AVClass. + * @param o an option for which the string is to be evaluated. + * @param val string to be evaluated. + * @param *_out value of the string will be written here. + * + * @return 0 on success, a negative number on failure. + */ +int av_opt_eval_flags (void *obj, const AVOption *o, const char *val, int *flags_out); +int av_opt_eval_int (void *obj, const AVOption *o, const char *val, int *int_out); +int av_opt_eval_int64 (void *obj, const AVOption *o, const char *val, int64_t *int64_out); +int av_opt_eval_float (void *obj, const AVOption *o, const char *val, float *float_out); +int av_opt_eval_double(void *obj, const AVOption *o, const char *val, double *double_out); +int av_opt_eval_q (void *obj, const AVOption *o, const char *val, AVRational *q_out); +/** + * @} + */ + +#define AV_OPT_SEARCH_CHILDREN (1 << 0) /**< Search in possible children of the + given object first. */ +/** + * The obj passed to av_opt_find() is fake -- only a double pointer to AVClass + * instead of a required pointer to a struct containing AVClass. This is + * useful for searching for options without needing to allocate the corresponding + * object. + */ +#define AV_OPT_SEARCH_FAKE_OBJ (1 << 1) + +/** + * In av_opt_get, return NULL if the option has a pointer type and is set to NULL, + * rather than returning an empty string. + */ +#define AV_OPT_ALLOW_NULL (1 << 2) + +/** + * Allows av_opt_query_ranges and av_opt_query_ranges_default to return more than + * one component for certain option types. + * @see AVOptionRanges for details. + */ +#define AV_OPT_MULTI_COMPONENT_RANGE (1 << 12) + +/** + * Look for an option in an object. Consider only options which + * have all the specified flags set. + * + * @param[in] obj A pointer to a struct whose first element is a + * pointer to an AVClass. + * Alternatively a double pointer to an AVClass, if + * AV_OPT_SEARCH_FAKE_OBJ search flag is set. + * @param[in] name The name of the option to look for. + * @param[in] unit When searching for named constants, name of the unit + * it belongs to. + * @param opt_flags Find only options with all the specified flags set (AV_OPT_FLAG). + * @param search_flags A combination of AV_OPT_SEARCH_*. + * + * @return A pointer to the option found, or NULL if no option + * was found. + * + * @note Options found with AV_OPT_SEARCH_CHILDREN flag may not be settable + * directly with av_opt_set(). Use special calls which take an options + * AVDictionary (e.g. avformat_open_input()) to set options found with this + * flag. + */ +const AVOption *av_opt_find(void *obj, const char *name, const char *unit, + int opt_flags, int search_flags); + +/** + * Look for an option in an object. Consider only options which + * have all the specified flags set. + * + * @param[in] obj A pointer to a struct whose first element is a + * pointer to an AVClass. + * Alternatively a double pointer to an AVClass, if + * AV_OPT_SEARCH_FAKE_OBJ search flag is set. + * @param[in] name The name of the option to look for. + * @param[in] unit When searching for named constants, name of the unit + * it belongs to. + * @param opt_flags Find only options with all the specified flags set (AV_OPT_FLAG). + * @param search_flags A combination of AV_OPT_SEARCH_*. + * @param[out] target_obj if non-NULL, an object to which the option belongs will be + * written here. It may be different from obj if AV_OPT_SEARCH_CHILDREN is present + * in search_flags. This parameter is ignored if search_flags contain + * AV_OPT_SEARCH_FAKE_OBJ. + * + * @return A pointer to the option found, or NULL if no option + * was found. + */ +const AVOption *av_opt_find2(void *obj, const char *name, const char *unit, + int opt_flags, int search_flags, void **target_obj); + +/** + * Iterate over all AVOptions belonging to obj. + * + * @param obj an AVOptions-enabled struct or a double pointer to an + * AVClass describing it. + * @param prev result of the previous call to av_opt_next() on this object + * or NULL + * @return next AVOption or NULL + */ +const AVOption *av_opt_next(const void *obj, const AVOption *prev); + +/** + * Iterate over AVOptions-enabled children of obj. + * + * @param prev result of a previous call to this function or NULL + * @return next AVOptions-enabled child or NULL + */ +void *av_opt_child_next(void *obj, void *prev); + +/** + * Iterate over potential AVOptions-enabled children of parent. + * + * @param prev result of a previous call to this function or NULL + * @return AVClass corresponding to next potential child or NULL + */ +const AVClass *av_opt_child_class_next(const AVClass *parent, const AVClass *prev); + +/** + * @defgroup opt_set_funcs Option setting functions + * @{ + * Those functions set the field of obj with the given name to value. + * + * @param[in] obj A struct whose first element is a pointer to an AVClass. + * @param[in] name the name of the field to set + * @param[in] val The value to set. In case of av_opt_set() if the field is not + * of a string type, then the given string is parsed. + * SI postfixes and some named scalars are supported. + * If the field is of a numeric type, it has to be a numeric or named + * scalar. Behavior with more than one scalar and +- infix operators + * is undefined. + * If the field is of a flags type, it has to be a sequence of numeric + * scalars or named flags separated by '+' or '-'. Prefixing a flag + * with '+' causes it to be set without affecting the other flags; + * similarly, '-' unsets a flag. + * @param search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN + * is passed here, then the option may be set on a child of obj. + * + * @return 0 if the value has been set, or an AVERROR code in case of + * error: + * AVERROR_OPTION_NOT_FOUND if no matching option exists + * AVERROR(ERANGE) if the value is out of range + * AVERROR(EINVAL) if the value is not valid + */ +int av_opt_set (void *obj, const char *name, const char *val, int search_flags); +int av_opt_set_int (void *obj, const char *name, int64_t val, int search_flags); +int av_opt_set_double (void *obj, const char *name, double val, int search_flags); +int av_opt_set_q (void *obj, const char *name, AVRational val, int search_flags); +int av_opt_set_bin (void *obj, const char *name, const uint8_t *val, int size, int search_flags); +int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_flags); +int av_opt_set_pixel_fmt (void *obj, const char *name, enum AVPixelFormat fmt, int search_flags); +int av_opt_set_sample_fmt(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags); +int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int search_flags); +int av_opt_set_channel_layout(void *obj, const char *name, int64_t ch_layout, int search_flags); +/** + * @note Any old dictionary present is discarded and replaced with a copy of the new one. The + * caller still owns val is and responsible for freeing it. + */ +int av_opt_set_dict_val(void *obj, const char *name, const AVDictionary *val, int search_flags); + +/** + * Set a binary option to an integer list. + * + * @param obj AVClass object to set options on + * @param name name of the binary option + * @param val pointer to an integer list (must have the correct type with + * regard to the contents of the list) + * @param term list terminator (usually 0 or -1) + * @param flags search flags + */ +#define av_opt_set_int_list(obj, name, val, term, flags) \ + (av_int_list_length(val, term) > INT_MAX / sizeof(*(val)) ? \ + AVERROR(EINVAL) : \ + av_opt_set_bin(obj, name, (const uint8_t *)(val), \ + av_int_list_length(val, term) * sizeof(*(val)), flags)) + +/** + * @} + */ + +/** + * @defgroup opt_get_funcs Option getting functions + * @{ + * Those functions get a value of the option with the given name from an object. + * + * @param[in] obj a struct whose first element is a pointer to an AVClass. + * @param[in] name name of the option to get. + * @param[in] search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN + * is passed here, then the option may be found in a child of obj. + * @param[out] out_val value of the option will be written here + * @return >=0 on success, a negative error code otherwise + */ +/** + * @note the returned string will be av_malloc()ed and must be av_free()ed by the caller + * + * @note if AV_OPT_ALLOW_NULL is set in search_flags in av_opt_get, and the option has + * AV_OPT_TYPE_STRING or AV_OPT_TYPE_BINARY and is set to NULL, *out_val will be set + * to NULL instead of an allocated empty string. + */ +int av_opt_get (void *obj, const char *name, int search_flags, uint8_t **out_val); +int av_opt_get_int (void *obj, const char *name, int search_flags, int64_t *out_val); +int av_opt_get_double (void *obj, const char *name, int search_flags, double *out_val); +int av_opt_get_q (void *obj, const char *name, int search_flags, AVRational *out_val); +int av_opt_get_image_size(void *obj, const char *name, int search_flags, int *w_out, int *h_out); +int av_opt_get_pixel_fmt (void *obj, const char *name, int search_flags, enum AVPixelFormat *out_fmt); +int av_opt_get_sample_fmt(void *obj, const char *name, int search_flags, enum AVSampleFormat *out_fmt); +int av_opt_get_video_rate(void *obj, const char *name, int search_flags, AVRational *out_val); +int av_opt_get_channel_layout(void *obj, const char *name, int search_flags, int64_t *ch_layout); +/** + * @param[out] out_val The returned dictionary is a copy of the actual value and must + * be freed with av_dict_free() by the caller + */ +int av_opt_get_dict_val(void *obj, const char *name, int search_flags, AVDictionary **out_val); +/** + * @} + */ +/** + * Gets a pointer to the requested field in a struct. + * This function allows accessing a struct even when its fields are moved or + * renamed since the application making the access has been compiled, + * + * @returns a pointer to the field, it can be cast to the correct type and read + * or written to. + */ +void *av_opt_ptr(const AVClass *avclass, void *obj, const char *name); + +/** + * Free an AVOptionRanges struct and set it to NULL. + */ +void av_opt_freep_ranges(AVOptionRanges **ranges); + +/** + * Get a list of allowed ranges for the given option. + * + * The returned list may depend on other fields in obj like for example profile. + * + * @param flags is a bitmask of flags, undefined flags should not be set and should be ignored + * AV_OPT_SEARCH_FAKE_OBJ indicates that the obj is a double pointer to a AVClass instead of a full instance + * AV_OPT_MULTI_COMPONENT_RANGE indicates that function may return more than one component, @see AVOptionRanges + * + * The result must be freed with av_opt_freep_ranges. + * + * @return number of compontents returned on success, a negative errro code otherwise + */ +int av_opt_query_ranges(AVOptionRanges **, void *obj, const char *key, int flags); + +/** + * Copy options from src object into dest object. + * + * Options that require memory allocation (e.g. string or binary) are malloc'ed in dest object. + * Original memory allocated for such options is freed unless both src and dest options points to the same memory. + * + * @param dest Object to copy from + * @param src Object to copy into + * @return 0 on success, negative on error + */ +int av_opt_copy(void *dest, const void *src); + +/** + * Get a default list of allowed ranges for the given option. + * + * This list is constructed without using the AVClass.query_ranges() callback + * and can be used as fallback from within the callback. + * + * @param flags is a bitmask of flags, undefined flags should not be set and should be ignored + * AV_OPT_SEARCH_FAKE_OBJ indicates that the obj is a double pointer to a AVClass instead of a full instance + * AV_OPT_MULTI_COMPONENT_RANGE indicates that function may return more than one component, @see AVOptionRanges + * + * The result must be freed with av_opt_free_ranges. + * + * @return number of compontents returned on success, a negative errro code otherwise + */ +int av_opt_query_ranges_default(AVOptionRanges **, void *obj, const char *key, int flags); + +/** + * Check if given option is set to its default value. + * + * Options o must belong to the obj. This function must not be called to check child's options state. + * @see av_opt_is_set_to_default_by_name(). + * + * @param obj AVClass object to check option on + * @param o option to be checked + * @return >0 when option is set to its default, + * 0 when option is not set its default, + * <0 on error + */ +int av_opt_is_set_to_default(void *obj, const AVOption *o); + +/** + * Check if given option is set to its default value. + * + * @param obj AVClass object to check option on + * @param name option name + * @param search_flags combination of AV_OPT_SEARCH_* + * @return >0 when option is set to its default, + * 0 when option is not set its default, + * <0 on error + */ +int av_opt_is_set_to_default_by_name(void *obj, const char *name, int search_flags); + + +#define AV_OPT_SERIALIZE_SKIP_DEFAULTS 0x00000001 ///< Serialize options that are not set to default values only. +#define AV_OPT_SERIALIZE_OPT_FLAGS_EXACT 0x00000002 ///< Serialize options that exactly match opt_flags only. + +/** + * Serialize object's options. + * + * Create a string containing object's serialized options. + * Such string may be passed back to av_opt_set_from_string() in order to restore option values. + * A key/value or pairs separator occurring in the serialized value or + * name string are escaped through the av_escape() function. + * + * @param[in] obj AVClass object to serialize + * @param[in] opt_flags serialize options with all the specified flags set (AV_OPT_FLAG) + * @param[in] flags combination of AV_OPT_SERIALIZE_* flags + * @param[out] buffer Pointer to buffer that will be allocated with string containg serialized options. + * Buffer must be freed by the caller when is no longer needed. + * @param[in] key_val_sep character used to separate key from value + * @param[in] pairs_sep character used to separate two pairs from each other + * @return >= 0 on success, negative on error + * @warning Separators cannot be neither '\\' nor '\0'. They also cannot be the same. + */ +int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer, + const char key_val_sep, const char pairs_sep); +/** + * @} + */ + +#endif /* AVUTIL_OPT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/parseutils.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/parseutils.h new file mode 100644 index 0000000..e66d24b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/parseutils.h
@@ -0,0 +1,193 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_PARSEUTILS_H +#define AVUTIL_PARSEUTILS_H + +#include <time.h> + +#include "rational.h" + +/** + * @file + * misc parsing utilities + */ + +/** + * Parse str and store the parsed ratio in q. + * + * Note that a ratio with infinite (1/0) or negative value is + * considered valid, so you should check on the returned value if you + * want to exclude those values. + * + * The undefined value can be expressed using the "0:0" string. + * + * @param[in,out] q pointer to the AVRational which will contain the ratio + * @param[in] str the string to parse: it has to be a string in the format + * num:den, a float number or an expression + * @param[in] max the maximum allowed numerator and denominator + * @param[in] log_offset log level offset which is applied to the log + * level of log_ctx + * @param[in] log_ctx parent logging context + * @return >= 0 on success, a negative error code otherwise + */ +int av_parse_ratio(AVRational *q, const char *str, int max, + int log_offset, void *log_ctx); + +#define av_parse_ratio_quiet(rate, str, max) \ + av_parse_ratio(rate, str, max, AV_LOG_MAX_OFFSET, NULL) + +/** + * Parse str and put in width_ptr and height_ptr the detected values. + * + * @param[in,out] width_ptr pointer to the variable which will contain the detected + * width value + * @param[in,out] height_ptr pointer to the variable which will contain the detected + * height value + * @param[in] str the string to parse: it has to be a string in the format + * width x height or a valid video size abbreviation. + * @return >= 0 on success, a negative error code otherwise + */ +int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str); + +/** + * Parse str and store the detected values in *rate. + * + * @param[in,out] rate pointer to the AVRational which will contain the detected + * frame rate + * @param[in] str the string to parse: it has to be a string in the format + * rate_num / rate_den, a float number or a valid video rate abbreviation + * @return >= 0 on success, a negative error code otherwise + */ +int av_parse_video_rate(AVRational *rate, const char *str); + +/** + * Put the RGBA values that correspond to color_string in rgba_color. + * + * @param color_string a string specifying a color. It can be the name of + * a color (case insensitive match) or a [0x|#]RRGGBB[AA] sequence, + * possibly followed by "@" and a string representing the alpha + * component. + * The alpha component may be a string composed by "0x" followed by an + * hexadecimal number or a decimal number between 0.0 and 1.0, which + * represents the opacity value (0x00/0.0 means completely transparent, + * 0xff/1.0 completely opaque). + * If the alpha component is not specified then 0xff is assumed. + * The string "random" will result in a random color. + * @param slen length of the initial part of color_string containing the + * color. It can be set to -1 if color_string is a null terminated string + * containing nothing else than the color. + * @return >= 0 in case of success, a negative value in case of + * failure (for example if color_string cannot be parsed). + */ +int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen, + void *log_ctx); + +/** + * Get the name of a color from the internal table of hard-coded named + * colors. + * + * This function is meant to enumerate the color names recognized by + * av_parse_color(). + * + * @param color_idx index of the requested color, starting from 0 + * @param rgbp if not NULL, will point to a 3-elements array with the color value in RGB + * @return the color name string or NULL if color_idx is not in the array + */ +const char *av_get_known_color_name(int color_idx, const uint8_t **rgb); + +/** + * Parse timestr and return in *time a corresponding number of + * microseconds. + * + * @param timeval puts here the number of microseconds corresponding + * to the string in timestr. If the string represents a duration, it + * is the number of microseconds contained in the time interval. If + * the string is a date, is the number of microseconds since 1st of + * January, 1970 up to the time of the parsed date. If timestr cannot + * be successfully parsed, set *time to INT64_MIN. + + * @param timestr a string representing a date or a duration. + * - If a date the syntax is: + * @code + * [{YYYY-MM-DD|YYYYMMDD}[T|t| ]]{{HH:MM:SS[.m...]]]}|{HHMMSS[.m...]]]}}[Z] + * now + * @endcode + * If the value is "now" it takes the current time. + * Time is local time unless Z is appended, in which case it is + * interpreted as UTC. + * If the year-month-day part is not specified it takes the current + * year-month-day. + * - If a duration the syntax is: + * @code + * [-][HH:]MM:SS[.m...] + * [-]S+[.m...] + * @endcode + * @param duration flag which tells how to interpret timestr, if not + * zero timestr is interpreted as a duration, otherwise as a date + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code otherwise + */ +int av_parse_time(int64_t *timeval, const char *timestr, int duration); + +/** + * Attempt to find a specific tag in a URL. + * + * syntax: '?tag1=val1&tag2=val2...'. Little URL decoding is done. + * Return 1 if found. + */ +int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info); + +/** + * Simplified version of strptime + * + * Parse the input string p according to the format string fmt and + * store its results in the structure dt. + * This implementation supports only a subset of the formats supported + * by the standard strptime(). + * + * The supported input field descriptors are listed below. + * - %H: the hour as a decimal number, using a 24-hour clock, in the + * range '00' through '23' + * - %J: hours as a decimal number, in the range '0' through INT_MAX + * - %M: the minute as a decimal number, using a 24-hour clock, in the + * range '00' through '59' + * - %S: the second as a decimal number, using a 24-hour clock, in the + * range '00' through '59' + * - %Y: the year as a decimal number, using the Gregorian calendar + * - %m: the month as a decimal number, in the range '1' through '12' + * - %d: the day of the month as a decimal number, in the range '1' + * through '31' + * - %T: alias for '%H:%M:%S' + * - %%: a literal '%' + * + * @return a pointer to the first character not processed in this function + * call. In case the input string contains more characters than + * required by the format string the return value points right after + * the last consumed input character. In case the whole input string + * is consumed the return value points to the null byte at the end of + * the string. On failure NULL is returned. + */ +char *av_small_strptime(const char *p, const char *fmt, struct tm *dt); + +/** + * Convert the decomposed UTC time in tm to a time_t value. + */ +time_t av_timegm(struct tm *tm); + +#endif /* AVUTIL_PARSEUTILS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixdesc.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixdesc.h new file mode 100644 index 0000000..c055810 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixdesc.h
@@ -0,0 +1,440 @@ +/* + * pixel format descriptor + * Copyright (c) 2009 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_PIXDESC_H +#define AVUTIL_PIXDESC_H + +#include <inttypes.h> + +#include "attributes.h" +#include "pixfmt.h" +#include "version.h" + +typedef struct AVComponentDescriptor { + /** + * Which of the 4 planes contains the component. + */ + int plane; + + /** + * Number of elements between 2 horizontally consecutive pixels. + * Elements are bits for bitstream formats, bytes otherwise. + */ + int step; + + /** + * Number of elements before the component of the first pixel. + * Elements are bits for bitstream formats, bytes otherwise. + */ + int offset; + + /** + * Number of least significant bits that must be shifted away + * to get the value. + */ + int shift; + + /** + * Number of bits in the component. + */ + int depth; + +#if FF_API_PLUS1_MINUS1 + /** deprecated, use step instead */ + attribute_deprecated int step_minus1; + + /** deprecated, use depth instead */ + attribute_deprecated int depth_minus1; + + /** deprecated, use offset instead */ + attribute_deprecated int offset_plus1; +#endif +} AVComponentDescriptor; + +/** + * Descriptor that unambiguously describes how the bits of a pixel are + * stored in the up to 4 data planes of an image. It also stores the + * subsampling factors and number of components. + * + * @note This is separate of the colorspace (RGB, YCbCr, YPbPr, JPEG-style YUV + * and all the YUV variants) AVPixFmtDescriptor just stores how values + * are stored not what these values represent. + */ +typedef struct AVPixFmtDescriptor { + const char *name; + uint8_t nb_components; ///< The number of components each pixel has, (1-4) + + /** + * Amount to shift the luma width right to find the chroma width. + * For YV12 this is 1 for example. + * chroma_width = AV_CEIL_RSHIFT(luma_width, log2_chroma_w) + * The note above is needed to ensure rounding up. + * This value only refers to the chroma components. + */ + uint8_t log2_chroma_w; + + /** + * Amount to shift the luma height right to find the chroma height. + * For YV12 this is 1 for example. + * chroma_height= AV_CEIL_RSHIFT(luma_height, log2_chroma_h) + * The note above is needed to ensure rounding up. + * This value only refers to the chroma components. + */ + uint8_t log2_chroma_h; + + /** + * Combination of AV_PIX_FMT_FLAG_... flags. + */ + uint64_t flags; + + /** + * Parameters that describe how pixels are packed. + * If the format has 1 or 2 components, then luma is 0. + * If the format has 3 or 4 components: + * if the RGB flag is set then 0 is red, 1 is green and 2 is blue; + * otherwise 0 is luma, 1 is chroma-U and 2 is chroma-V. + * + * If present, the Alpha channel is always the last component. + */ + AVComponentDescriptor comp[4]; + + /** + * Alternative comma-separated names. + */ + const char *alias; +} AVPixFmtDescriptor; + +/** + * Pixel format is big-endian. + */ +#define AV_PIX_FMT_FLAG_BE (1 << 0) +/** + * Pixel format has a palette in data[1], values are indexes in this palette. + */ +#define AV_PIX_FMT_FLAG_PAL (1 << 1) +/** + * All values of a component are bit-wise packed end to end. + */ +#define AV_PIX_FMT_FLAG_BITSTREAM (1 << 2) +/** + * Pixel format is an HW accelerated format. + */ +#define AV_PIX_FMT_FLAG_HWACCEL (1 << 3) +/** + * At least one pixel component is not in the first data plane. + */ +#define AV_PIX_FMT_FLAG_PLANAR (1 << 4) +/** + * The pixel format contains RGB-like data (as opposed to YUV/grayscale). + */ +#define AV_PIX_FMT_FLAG_RGB (1 << 5) + +/** + * The pixel format is "pseudo-paletted". This means that it contains a + * fixed palette in the 2nd plane but the palette is fixed/constant for each + * PIX_FMT. This allows interpreting the data as if it was PAL8, which can + * in some cases be simpler. Or the data can be interpreted purely based on + * the pixel format without using the palette. + * An example of a pseudo-paletted format is AV_PIX_FMT_GRAY8 + * + * @deprecated This flag is deprecated, and will be removed. When it is removed, + * the extra palette allocation in AVFrame.data[1] is removed as well. Only + * actual paletted formats (as indicated by AV_PIX_FMT_FLAG_PAL) will have a + * palette. Starting with FFmpeg versions which have this flag deprecated, the + * extra "pseudo" palette is already ignored, and API users are not required to + * allocate a palette for AV_PIX_FMT_FLAG_PSEUDOPAL formats (it was required + * before the deprecation, though). + */ +#define AV_PIX_FMT_FLAG_PSEUDOPAL (1 << 6) + +/** + * The pixel format has an alpha channel. This is set on all formats that + * support alpha in some way, including AV_PIX_FMT_PAL8. The alpha is always + * straight, never pre-multiplied. + * + * If a codec or a filter does not support alpha, it should set all alpha to + * opaque, or use the equivalent pixel formats without alpha component, e.g. + * AV_PIX_FMT_RGB0 (or AV_PIX_FMT_RGB24 etc.) instead of AV_PIX_FMT_RGBA. + */ +#define AV_PIX_FMT_FLAG_ALPHA (1 << 7) + +/** + * The pixel format is following a Bayer pattern + */ +#define AV_PIX_FMT_FLAG_BAYER (1 << 8) + +/** + * The pixel format contains IEEE-754 floating point values. Precision (double, + * single, or half) should be determined by the pixel size (64, 32, or 16 bits). + */ +#define AV_PIX_FMT_FLAG_FLOAT (1 << 9) + +/** + * Return the number of bits per pixel used by the pixel format + * described by pixdesc. Note that this is not the same as the number + * of bits per sample. + * + * The returned number of bits refers to the number of bits actually + * used for storing the pixel information, that is padding bits are + * not counted. + */ +int av_get_bits_per_pixel(const AVPixFmtDescriptor *pixdesc); + +/** + * Return the number of bits per pixel for the pixel format + * described by pixdesc, including any padding or unused bits. + */ +int av_get_padded_bits_per_pixel(const AVPixFmtDescriptor *pixdesc); + +/** + * @return a pixel format descriptor for provided pixel format or NULL if + * this pixel format is unknown. + */ +const AVPixFmtDescriptor *av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt); + +/** + * Iterate over all pixel format descriptors known to libavutil. + * + * @param prev previous descriptor. NULL to get the first descriptor. + * + * @return next descriptor or NULL after the last descriptor + */ +const AVPixFmtDescriptor *av_pix_fmt_desc_next(const AVPixFmtDescriptor *prev); + +/** + * @return an AVPixelFormat id described by desc, or AV_PIX_FMT_NONE if desc + * is not a valid pointer to a pixel format descriptor. + */ +enum AVPixelFormat av_pix_fmt_desc_get_id(const AVPixFmtDescriptor *desc); + +/** + * Utility function to access log2_chroma_w log2_chroma_h from + * the pixel format AVPixFmtDescriptor. + * + * @param[in] pix_fmt the pixel format + * @param[out] h_shift store log2_chroma_w (horizontal/width shift) + * @param[out] v_shift store log2_chroma_h (vertical/height shift) + * + * @return 0 on success, AVERROR(ENOSYS) on invalid or unknown pixel format + */ +int av_pix_fmt_get_chroma_sub_sample(enum AVPixelFormat pix_fmt, + int *h_shift, int *v_shift); + +/** + * @return number of planes in pix_fmt, a negative AVERROR if pix_fmt is not a + * valid pixel format. + */ +int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt); + +/** + * @return the name for provided color range or NULL if unknown. + */ +const char *av_color_range_name(enum AVColorRange range); + +/** + * @return the AVColorRange value for name or an AVError if not found. + */ +int av_color_range_from_name(const char *name); + +/** + * @return the name for provided color primaries or NULL if unknown. + */ +const char *av_color_primaries_name(enum AVColorPrimaries primaries); + +/** + * @return the AVColorPrimaries value for name or an AVError if not found. + */ +int av_color_primaries_from_name(const char *name); + +/** + * @return the name for provided color transfer or NULL if unknown. + */ +const char *av_color_transfer_name(enum AVColorTransferCharacteristic transfer); + +/** + * @return the AVColorTransferCharacteristic value for name or an AVError if not found. + */ +int av_color_transfer_from_name(const char *name); + +/** + * @return the name for provided color space or NULL if unknown. + */ +const char *av_color_space_name(enum AVColorSpace space); + +/** + * @return the AVColorSpace value for name or an AVError if not found. + */ +int av_color_space_from_name(const char *name); + +/** + * @return the name for provided chroma location or NULL if unknown. + */ +const char *av_chroma_location_name(enum AVChromaLocation location); + +/** + * @return the AVChromaLocation value for name or an AVError if not found. + */ +int av_chroma_location_from_name(const char *name); + +/** + * Return the pixel format corresponding to name. + * + * If there is no pixel format with name name, then looks for a + * pixel format with the name corresponding to the native endian + * format of name. + * For example in a little-endian system, first looks for "gray16", + * then for "gray16le". + * + * Finally if no pixel format has been found, returns AV_PIX_FMT_NONE. + */ +enum AVPixelFormat av_get_pix_fmt(const char *name); + +/** + * Return the short name for a pixel format, NULL in case pix_fmt is + * unknown. + * + * @see av_get_pix_fmt(), av_get_pix_fmt_string() + */ +const char *av_get_pix_fmt_name(enum AVPixelFormat pix_fmt); + +/** + * Print in buf the string corresponding to the pixel format with + * number pix_fmt, or a header if pix_fmt is negative. + * + * @param buf the buffer where to write the string + * @param buf_size the size of buf + * @param pix_fmt the number of the pixel format to print the + * corresponding info string, or a negative value to print the + * corresponding header. + */ +char *av_get_pix_fmt_string(char *buf, int buf_size, + enum AVPixelFormat pix_fmt); + +/** + * Read a line from an image, and write the values of the + * pixel format component c to dst. + * + * @param data the array containing the pointers to the planes of the image + * @param linesize the array containing the linesizes of the image + * @param desc the pixel format descriptor for the image + * @param x the horizontal coordinate of the first pixel to read + * @param y the vertical coordinate of the first pixel to read + * @param w the width of the line to read, that is the number of + * values to write to dst + * @param read_pal_component if not zero and the format is a paletted + * format writes the values corresponding to the palette + * component c in data[1] to dst, rather than the palette indexes in + * data[0]. The behavior is undefined if the format is not paletted. + * @param dst_element_size size of elements in dst array (2 or 4 byte) + */ +void av_read_image_line2(void *dst, const uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w, int read_pal_component, + int dst_element_size); + +void av_read_image_line(uint16_t *dst, const uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w, int read_pal_component); + +/** + * Write the values from src to the pixel format component c of an + * image line. + * + * @param src array containing the values to write + * @param data the array containing the pointers to the planes of the + * image to write into. It is supposed to be zeroed. + * @param linesize the array containing the linesizes of the image + * @param desc the pixel format descriptor for the image + * @param x the horizontal coordinate of the first pixel to write + * @param y the vertical coordinate of the first pixel to write + * @param w the width of the line to write, that is the number of + * values to write to the image line + * @param src_element_size size of elements in src array (2 or 4 byte) + */ +void av_write_image_line2(const void *src, uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w, int src_element_size); + +void av_write_image_line(const uint16_t *src, uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w); + +/** + * Utility function to swap the endianness of a pixel format. + * + * @param[in] pix_fmt the pixel format + * + * @return pixel format with swapped endianness if it exists, + * otherwise AV_PIX_FMT_NONE + */ +enum AVPixelFormat av_pix_fmt_swap_endianness(enum AVPixelFormat pix_fmt); + +#define FF_LOSS_RESOLUTION 0x0001 /**< loss due to resolution change */ +#define FF_LOSS_DEPTH 0x0002 /**< loss due to color depth change */ +#define FF_LOSS_COLORSPACE 0x0004 /**< loss due to color space conversion */ +#define FF_LOSS_ALPHA 0x0008 /**< loss of alpha bits */ +#define FF_LOSS_COLORQUANT 0x0010 /**< loss due to color quantization */ +#define FF_LOSS_CHROMA 0x0020 /**< loss of chroma (e.g. RGB to gray conversion) */ + +/** + * Compute what kind of losses will occur when converting from one specific + * pixel format to another. + * When converting from one pixel format to another, information loss may occur. + * For example, when converting from RGB24 to GRAY, the color information will + * be lost. Similarly, other losses occur when converting from some formats to + * other formats. These losses can involve loss of chroma, but also loss of + * resolution, loss of color depth, loss due to the color space conversion, loss + * of the alpha bits or loss due to color quantization. + * av_get_fix_fmt_loss() informs you about the various types of losses + * which will occur when converting from one pixel format to another. + * + * @param[in] dst_pix_fmt destination pixel format + * @param[in] src_pix_fmt source pixel format + * @param[in] has_alpha Whether the source pixel format alpha channel is used. + * @return Combination of flags informing you what kind of losses will occur + * (maximum loss for an invalid dst_pix_fmt). + */ +int av_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, + enum AVPixelFormat src_pix_fmt, + int has_alpha); + +/** + * Compute what kind of losses will occur when converting from one specific + * pixel format to another. + * When converting from one pixel format to another, information loss may occur. + * For example, when converting from RGB24 to GRAY, the color information will + * be lost. Similarly, other losses occur when converting from some formats to + * other formats. These losses can involve loss of chroma, but also loss of + * resolution, loss of color depth, loss due to the color space conversion, loss + * of the alpha bits or loss due to color quantization. + * av_get_fix_fmt_loss() informs you about the various types of losses + * which will occur when converting from one pixel format to another. + * + * @param[in] dst_pix_fmt destination pixel format + * @param[in] src_pix_fmt source pixel format + * @param[in] has_alpha Whether the source pixel format alpha channel is used. + * @return Combination of flags informing you what kind of losses will occur + * (maximum loss for an invalid dst_pix_fmt). + */ +enum AVPixelFormat av_find_best_pix_fmt_of_2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, + enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr); + +#endif /* AVUTIL_PIXDESC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixelutils.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixelutils.h new file mode 100644 index 0000000..a8dbc15 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixelutils.h
@@ -0,0 +1,52 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_PIXELUTILS_H +#define AVUTIL_PIXELUTILS_H + +#include <stddef.h> +#include <stdint.h> +#include "common.h" + +/** + * Sum of abs(src1[x] - src2[x]) + */ +typedef int (*av_pixelutils_sad_fn)(const uint8_t *src1, ptrdiff_t stride1, + const uint8_t *src2, ptrdiff_t stride2); + +/** + * Get a potentially optimized pointer to a Sum-of-absolute-differences + * function (see the av_pixelutils_sad_fn prototype). + * + * @param w_bits 1<<w_bits is the requested width of the block size + * @param h_bits 1<<h_bits is the requested height of the block size + * @param aligned If set to 2, the returned sad function will assume src1 and + * src2 addresses are aligned on the block size. + * If set to 1, the returned sad function will assume src1 is + * aligned on the block size. + * If set to 0, the returned sad function assume no particular + * alignment. + * @param log_ctx context used for logging, can be NULL + * + * @return a pointer to the SAD function or NULL in case of error (because of + * invalid parameters) + */ +av_pixelutils_sad_fn av_pixelutils_get_sad_fn(int w_bits, int h_bits, + int aligned, void *log_ctx); + +#endif /* AVUTIL_PIXELUTILS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixfmt.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixfmt.h new file mode 100644 index 0000000..6815f8d --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixfmt.h
@@ -0,0 +1,542 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_PIXFMT_H +#define AVUTIL_PIXFMT_H + +/** + * @file + * pixel format definitions + */ + +#include "libavutil/avconfig.h" +#include "version.h" + +#define AVPALETTE_SIZE 1024 +#define AVPALETTE_COUNT 256 + +/** + * Pixel format. + * + * @note + * AV_PIX_FMT_RGB32 is handled in an endian-specific manner. An RGBA + * color is put together as: + * (A << 24) | (R << 16) | (G << 8) | B + * This is stored as BGRA on little-endian CPU architectures and ARGB on + * big-endian CPUs. + * + * @note + * If the resolution is not a multiple of the chroma subsampling factor + * then the chroma plane resolution must be rounded up. + * + * @par + * When the pixel format is palettized RGB32 (AV_PIX_FMT_PAL8), the palettized + * image data is stored in AVFrame.data[0]. The palette is transported in + * AVFrame.data[1], is 1024 bytes long (256 4-byte entries) and is + * formatted the same as in AV_PIX_FMT_RGB32 described above (i.e., it is + * also endian-specific). Note also that the individual RGB32 palette + * components stored in AVFrame.data[1] should be in the range 0..255. + * This is important as many custom PAL8 video codecs that were designed + * to run on the IBM VGA graphics adapter use 6-bit palette components. + * + * @par + * For all the 8 bits per pixel formats, an RGB32 palette is in data[1] like + * for pal8. This palette is filled in automatically by the function + * allocating the picture. + */ +enum AVPixelFormat { + AV_PIX_FMT_NONE = -1, + AV_PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) + AV_PIX_FMT_YUYV422, ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr + AV_PIX_FMT_RGB24, ///< packed RGB 8:8:8, 24bpp, RGBRGB... + AV_PIX_FMT_BGR24, ///< packed RGB 8:8:8, 24bpp, BGRBGR... + AV_PIX_FMT_YUV422P, ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) + AV_PIX_FMT_YUV444P, ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) + AV_PIX_FMT_YUV410P, ///< planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) + AV_PIX_FMT_YUV411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) + AV_PIX_FMT_GRAY8, ///< Y , 8bpp + AV_PIX_FMT_MONOWHITE, ///< Y , 1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb + AV_PIX_FMT_MONOBLACK, ///< Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb + AV_PIX_FMT_PAL8, ///< 8 bits with AV_PIX_FMT_RGB32 palette + AV_PIX_FMT_YUVJ420P, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting color_range + AV_PIX_FMT_YUVJ422P, ///< planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting color_range + AV_PIX_FMT_YUVJ444P, ///< planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting color_range + AV_PIX_FMT_UYVY422, ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 + AV_PIX_FMT_UYYVYY411, ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 + AV_PIX_FMT_BGR8, ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) + AV_PIX_FMT_BGR4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits + AV_PIX_FMT_BGR4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) + AV_PIX_FMT_RGB8, ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) + AV_PIX_FMT_RGB4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits + AV_PIX_FMT_RGB4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) + AV_PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) + AV_PIX_FMT_NV21, ///< as above, but U and V bytes are swapped + + AV_PIX_FMT_ARGB, ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB... + AV_PIX_FMT_RGBA, ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA... + AV_PIX_FMT_ABGR, ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR... + AV_PIX_FMT_BGRA, ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA... + + AV_PIX_FMT_GRAY16BE, ///< Y , 16bpp, big-endian + AV_PIX_FMT_GRAY16LE, ///< Y , 16bpp, little-endian + AV_PIX_FMT_YUV440P, ///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples) + AV_PIX_FMT_YUVJ440P, ///< planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range + AV_PIX_FMT_YUVA420P, ///< planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples) + AV_PIX_FMT_RGB48BE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian + AV_PIX_FMT_RGB48LE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as little-endian + + AV_PIX_FMT_RGB565BE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), big-endian + AV_PIX_FMT_RGB565LE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), little-endian + AV_PIX_FMT_RGB555BE, ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), big-endian , X=unused/undefined + AV_PIX_FMT_RGB555LE, ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), little-endian, X=unused/undefined + + AV_PIX_FMT_BGR565BE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), big-endian + AV_PIX_FMT_BGR565LE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), little-endian + AV_PIX_FMT_BGR555BE, ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), big-endian , X=unused/undefined + AV_PIX_FMT_BGR555LE, ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), little-endian, X=unused/undefined + +#if FF_API_VAAPI + /** @name Deprecated pixel formats */ + /**@{*/ + AV_PIX_FMT_VAAPI_MOCO, ///< HW acceleration through VA API at motion compensation entry-point, Picture.data[3] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers + AV_PIX_FMT_VAAPI_IDCT, ///< HW acceleration through VA API at IDCT entry-point, Picture.data[3] contains a vaapi_render_state struct which contains fields extracted from headers + AV_PIX_FMT_VAAPI_VLD, ///< HW decoding through VA API, Picture.data[3] contains a VASurfaceID + /**@}*/ + AV_PIX_FMT_VAAPI = AV_PIX_FMT_VAAPI_VLD, +#else + /** + * Hardware acceleration through VA-API, data[3] contains a + * VASurfaceID. + */ + AV_PIX_FMT_VAAPI, +#endif + + AV_PIX_FMT_YUV420P16LE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV420P16BE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV422P16LE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_YUV422P16BE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV444P16LE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_YUV444P16BE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_DXVA2_VLD, ///< HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer + + AV_PIX_FMT_RGB444LE, ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), little-endian, X=unused/undefined + AV_PIX_FMT_RGB444BE, ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), big-endian, X=unused/undefined + AV_PIX_FMT_BGR444LE, ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), little-endian, X=unused/undefined + AV_PIX_FMT_BGR444BE, ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), big-endian, X=unused/undefined + AV_PIX_FMT_YA8, ///< 8 bits gray, 8 bits alpha + + AV_PIX_FMT_Y400A = AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8 + AV_PIX_FMT_GRAY8A= AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8 + + AV_PIX_FMT_BGR48BE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as big-endian + AV_PIX_FMT_BGR48LE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as little-endian + + /** + * The following 12 formats have the disadvantage of needing 1 format for each bit depth. + * Notice that each 9/10 bits sample is stored in 16 bits with extra padding. + * If you want to support multiple bit depths, then using AV_PIX_FMT_YUV420P16* with the bpp stored separately is better. + */ + AV_PIX_FMT_YUV420P9BE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV420P9LE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV420P10BE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV420P10LE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV422P10BE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV422P10LE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_YUV444P9BE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_YUV444P9LE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_YUV444P10BE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_YUV444P10LE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_YUV422P9BE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV422P9LE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_GBRP, ///< planar GBR 4:4:4 24bpp + AV_PIX_FMT_GBR24P = AV_PIX_FMT_GBRP, // alias for #AV_PIX_FMT_GBRP + AV_PIX_FMT_GBRP9BE, ///< planar GBR 4:4:4 27bpp, big-endian + AV_PIX_FMT_GBRP9LE, ///< planar GBR 4:4:4 27bpp, little-endian + AV_PIX_FMT_GBRP10BE, ///< planar GBR 4:4:4 30bpp, big-endian + AV_PIX_FMT_GBRP10LE, ///< planar GBR 4:4:4 30bpp, little-endian + AV_PIX_FMT_GBRP16BE, ///< planar GBR 4:4:4 48bpp, big-endian + AV_PIX_FMT_GBRP16LE, ///< planar GBR 4:4:4 48bpp, little-endian + AV_PIX_FMT_YUVA422P, ///< planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples) + AV_PIX_FMT_YUVA444P, ///< planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples) + AV_PIX_FMT_YUVA420P9BE, ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), big-endian + AV_PIX_FMT_YUVA420P9LE, ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), little-endian + AV_PIX_FMT_YUVA422P9BE, ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), big-endian + AV_PIX_FMT_YUVA422P9LE, ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), little-endian + AV_PIX_FMT_YUVA444P9BE, ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), big-endian + AV_PIX_FMT_YUVA444P9LE, ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), little-endian + AV_PIX_FMT_YUVA420P10BE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian) + AV_PIX_FMT_YUVA420P10LE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian) + AV_PIX_FMT_YUVA422P10BE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian) + AV_PIX_FMT_YUVA422P10LE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian) + AV_PIX_FMT_YUVA444P10BE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian) + AV_PIX_FMT_YUVA444P10LE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian) + AV_PIX_FMT_YUVA420P16BE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian) + AV_PIX_FMT_YUVA420P16LE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian) + AV_PIX_FMT_YUVA422P16BE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian) + AV_PIX_FMT_YUVA422P16LE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian) + AV_PIX_FMT_YUVA444P16BE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian) + AV_PIX_FMT_YUVA444P16LE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian) + + AV_PIX_FMT_VDPAU, ///< HW acceleration through VDPAU, Picture.data[3] contains a VdpVideoSurface + + AV_PIX_FMT_XYZ12LE, ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as little-endian, the 4 lower bits are set to 0 + AV_PIX_FMT_XYZ12BE, ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as big-endian, the 4 lower bits are set to 0 + AV_PIX_FMT_NV16, ///< interleaved chroma YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) + AV_PIX_FMT_NV20LE, ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_NV20BE, ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + + AV_PIX_FMT_RGBA64BE, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian + AV_PIX_FMT_RGBA64LE, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian + AV_PIX_FMT_BGRA64BE, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian + AV_PIX_FMT_BGRA64LE, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian + + AV_PIX_FMT_YVYU422, ///< packed YUV 4:2:2, 16bpp, Y0 Cr Y1 Cb + + AV_PIX_FMT_YA16BE, ///< 16 bits gray, 16 bits alpha (big-endian) + AV_PIX_FMT_YA16LE, ///< 16 bits gray, 16 bits alpha (little-endian) + + AV_PIX_FMT_GBRAP, ///< planar GBRA 4:4:4:4 32bpp + AV_PIX_FMT_GBRAP16BE, ///< planar GBRA 4:4:4:4 64bpp, big-endian + AV_PIX_FMT_GBRAP16LE, ///< planar GBRA 4:4:4:4 64bpp, little-endian + /** + * HW acceleration through QSV, data[3] contains a pointer to the + * mfxFrameSurface1 structure. + */ + AV_PIX_FMT_QSV, + /** + * HW acceleration though MMAL, data[3] contains a pointer to the + * MMAL_BUFFER_HEADER_T structure. + */ + AV_PIX_FMT_MMAL, + + AV_PIX_FMT_D3D11VA_VLD, ///< HW decoding through Direct3D11 via old API, Picture.data[3] contains a ID3D11VideoDecoderOutputView pointer + + /** + * HW acceleration through CUDA. data[i] contain CUdeviceptr pointers + * exactly as for system memory frames. + */ + AV_PIX_FMT_CUDA, + + AV_PIX_FMT_0RGB, ///< packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined + AV_PIX_FMT_RGB0, ///< packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined + AV_PIX_FMT_0BGR, ///< packed BGR 8:8:8, 32bpp, XBGRXBGR... X=unused/undefined + AV_PIX_FMT_BGR0, ///< packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined + + AV_PIX_FMT_YUV420P12BE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV420P12LE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV420P14BE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV420P14LE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_YUV422P14BE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV422P14LE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_YUV444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_YUV444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_YUV444P14BE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_YUV444P14LE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_GBRP12BE, ///< planar GBR 4:4:4 36bpp, big-endian + AV_PIX_FMT_GBRP12LE, ///< planar GBR 4:4:4 36bpp, little-endian + AV_PIX_FMT_GBRP14BE, ///< planar GBR 4:4:4 42bpp, big-endian + AV_PIX_FMT_GBRP14LE, ///< planar GBR 4:4:4 42bpp, little-endian + AV_PIX_FMT_YUVJ411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV411P and setting color_range + + AV_PIX_FMT_BAYER_BGGR8, ///< bayer, BGBG..(odd line), GRGR..(even line), 8-bit samples */ + AV_PIX_FMT_BAYER_RGGB8, ///< bayer, RGRG..(odd line), GBGB..(even line), 8-bit samples */ + AV_PIX_FMT_BAYER_GBRG8, ///< bayer, GBGB..(odd line), RGRG..(even line), 8-bit samples */ + AV_PIX_FMT_BAYER_GRBG8, ///< bayer, GRGR..(odd line), BGBG..(even line), 8-bit samples */ + AV_PIX_FMT_BAYER_BGGR16LE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, little-endian */ + AV_PIX_FMT_BAYER_BGGR16BE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, big-endian */ + AV_PIX_FMT_BAYER_RGGB16LE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, little-endian */ + AV_PIX_FMT_BAYER_RGGB16BE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, big-endian */ + AV_PIX_FMT_BAYER_GBRG16LE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, little-endian */ + AV_PIX_FMT_BAYER_GBRG16BE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, big-endian */ + AV_PIX_FMT_BAYER_GRBG16LE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, little-endian */ + AV_PIX_FMT_BAYER_GRBG16BE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, big-endian */ + + AV_PIX_FMT_XVMC,///< XVideo Motion Acceleration via common packet passing + + AV_PIX_FMT_YUV440P10LE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian + AV_PIX_FMT_YUV440P10BE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian + AV_PIX_FMT_YUV440P12LE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian + AV_PIX_FMT_YUV440P12BE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian + AV_PIX_FMT_AYUV64LE, ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), little-endian + AV_PIX_FMT_AYUV64BE, ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), big-endian + + AV_PIX_FMT_VIDEOTOOLBOX, ///< hardware decoding through Videotoolbox + + AV_PIX_FMT_P010LE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, little-endian + AV_PIX_FMT_P010BE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, big-endian + + AV_PIX_FMT_GBRAP12BE, ///< planar GBR 4:4:4:4 48bpp, big-endian + AV_PIX_FMT_GBRAP12LE, ///< planar GBR 4:4:4:4 48bpp, little-endian + + AV_PIX_FMT_GBRAP10BE, ///< planar GBR 4:4:4:4 40bpp, big-endian + AV_PIX_FMT_GBRAP10LE, ///< planar GBR 4:4:4:4 40bpp, little-endian + + AV_PIX_FMT_MEDIACODEC, ///< hardware decoding through MediaCodec + + AV_PIX_FMT_GRAY12BE, ///< Y , 12bpp, big-endian + AV_PIX_FMT_GRAY12LE, ///< Y , 12bpp, little-endian + AV_PIX_FMT_GRAY10BE, ///< Y , 10bpp, big-endian + AV_PIX_FMT_GRAY10LE, ///< Y , 10bpp, little-endian + + AV_PIX_FMT_P016LE, ///< like NV12, with 16bpp per component, little-endian + AV_PIX_FMT_P016BE, ///< like NV12, with 16bpp per component, big-endian + + /** + * Hardware surfaces for Direct3D11. + * + * This is preferred over the legacy AV_PIX_FMT_D3D11VA_VLD. The new D3D11 + * hwaccel API and filtering support AV_PIX_FMT_D3D11 only. + * + * data[0] contains a ID3D11Texture2D pointer, and data[1] contains the + * texture array index of the frame as intptr_t if the ID3D11Texture2D is + * an array texture (or always 0 if it's a normal texture). + */ + AV_PIX_FMT_D3D11, + + AV_PIX_FMT_GRAY9BE, ///< Y , 9bpp, big-endian + AV_PIX_FMT_GRAY9LE, ///< Y , 9bpp, little-endian + + AV_PIX_FMT_GBRPF32BE, ///< IEEE-754 single precision planar GBR 4:4:4, 96bpp, big-endian + AV_PIX_FMT_GBRPF32LE, ///< IEEE-754 single precision planar GBR 4:4:4, 96bpp, little-endian + AV_PIX_FMT_GBRAPF32BE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, big-endian + AV_PIX_FMT_GBRAPF32LE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, little-endian + + /** + * DRM-managed buffers exposed through PRIME buffer sharing. + * + * data[0] points to an AVDRMFrameDescriptor. + */ + AV_PIX_FMT_DRM_PRIME, + /** + * Hardware surfaces for OpenCL. + * + * data[i] contain 2D image objects (typed in C as cl_mem, used + * in OpenCL as image2d_t) for each plane of the surface. + */ + AV_PIX_FMT_OPENCL, + + AV_PIX_FMT_GRAY14BE, ///< Y , 14bpp, big-endian + AV_PIX_FMT_GRAY14LE, ///< Y , 14bpp, little-endian + + AV_PIX_FMT_GRAYF32BE, ///< IEEE-754 single precision Y, 32bpp, big-endian + AV_PIX_FMT_GRAYF32LE, ///< IEEE-754 single precision Y, 32bpp, little-endian + + AV_PIX_FMT_NB ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions +}; + +#if AV_HAVE_BIGENDIAN +# define AV_PIX_FMT_NE(be, le) AV_PIX_FMT_##be +#else +# define AV_PIX_FMT_NE(be, le) AV_PIX_FMT_##le +#endif + +#define AV_PIX_FMT_RGB32 AV_PIX_FMT_NE(ARGB, BGRA) +#define AV_PIX_FMT_RGB32_1 AV_PIX_FMT_NE(RGBA, ABGR) +#define AV_PIX_FMT_BGR32 AV_PIX_FMT_NE(ABGR, RGBA) +#define AV_PIX_FMT_BGR32_1 AV_PIX_FMT_NE(BGRA, ARGB) +#define AV_PIX_FMT_0RGB32 AV_PIX_FMT_NE(0RGB, BGR0) +#define AV_PIX_FMT_0BGR32 AV_PIX_FMT_NE(0BGR, RGB0) + +#define AV_PIX_FMT_GRAY9 AV_PIX_FMT_NE(GRAY9BE, GRAY9LE) +#define AV_PIX_FMT_GRAY10 AV_PIX_FMT_NE(GRAY10BE, GRAY10LE) +#define AV_PIX_FMT_GRAY12 AV_PIX_FMT_NE(GRAY12BE, GRAY12LE) +#define AV_PIX_FMT_GRAY14 AV_PIX_FMT_NE(GRAY14BE, GRAY14LE) +#define AV_PIX_FMT_GRAY16 AV_PIX_FMT_NE(GRAY16BE, GRAY16LE) +#define AV_PIX_FMT_YA16 AV_PIX_FMT_NE(YA16BE, YA16LE) +#define AV_PIX_FMT_RGB48 AV_PIX_FMT_NE(RGB48BE, RGB48LE) +#define AV_PIX_FMT_RGB565 AV_PIX_FMT_NE(RGB565BE, RGB565LE) +#define AV_PIX_FMT_RGB555 AV_PIX_FMT_NE(RGB555BE, RGB555LE) +#define AV_PIX_FMT_RGB444 AV_PIX_FMT_NE(RGB444BE, RGB444LE) +#define AV_PIX_FMT_RGBA64 AV_PIX_FMT_NE(RGBA64BE, RGBA64LE) +#define AV_PIX_FMT_BGR48 AV_PIX_FMT_NE(BGR48BE, BGR48LE) +#define AV_PIX_FMT_BGR565 AV_PIX_FMT_NE(BGR565BE, BGR565LE) +#define AV_PIX_FMT_BGR555 AV_PIX_FMT_NE(BGR555BE, BGR555LE) +#define AV_PIX_FMT_BGR444 AV_PIX_FMT_NE(BGR444BE, BGR444LE) +#define AV_PIX_FMT_BGRA64 AV_PIX_FMT_NE(BGRA64BE, BGRA64LE) + +#define AV_PIX_FMT_YUV420P9 AV_PIX_FMT_NE(YUV420P9BE , YUV420P9LE) +#define AV_PIX_FMT_YUV422P9 AV_PIX_FMT_NE(YUV422P9BE , YUV422P9LE) +#define AV_PIX_FMT_YUV444P9 AV_PIX_FMT_NE(YUV444P9BE , YUV444P9LE) +#define AV_PIX_FMT_YUV420P10 AV_PIX_FMT_NE(YUV420P10BE, YUV420P10LE) +#define AV_PIX_FMT_YUV422P10 AV_PIX_FMT_NE(YUV422P10BE, YUV422P10LE) +#define AV_PIX_FMT_YUV440P10 AV_PIX_FMT_NE(YUV440P10BE, YUV440P10LE) +#define AV_PIX_FMT_YUV444P10 AV_PIX_FMT_NE(YUV444P10BE, YUV444P10LE) +#define AV_PIX_FMT_YUV420P12 AV_PIX_FMT_NE(YUV420P12BE, YUV420P12LE) +#define AV_PIX_FMT_YUV422P12 AV_PIX_FMT_NE(YUV422P12BE, YUV422P12LE) +#define AV_PIX_FMT_YUV440P12 AV_PIX_FMT_NE(YUV440P12BE, YUV440P12LE) +#define AV_PIX_FMT_YUV444P12 AV_PIX_FMT_NE(YUV444P12BE, YUV444P12LE) +#define AV_PIX_FMT_YUV420P14 AV_PIX_FMT_NE(YUV420P14BE, YUV420P14LE) +#define AV_PIX_FMT_YUV422P14 AV_PIX_FMT_NE(YUV422P14BE, YUV422P14LE) +#define AV_PIX_FMT_YUV444P14 AV_PIX_FMT_NE(YUV444P14BE, YUV444P14LE) +#define AV_PIX_FMT_YUV420P16 AV_PIX_FMT_NE(YUV420P16BE, YUV420P16LE) +#define AV_PIX_FMT_YUV422P16 AV_PIX_FMT_NE(YUV422P16BE, YUV422P16LE) +#define AV_PIX_FMT_YUV444P16 AV_PIX_FMT_NE(YUV444P16BE, YUV444P16LE) + +#define AV_PIX_FMT_GBRP9 AV_PIX_FMT_NE(GBRP9BE , GBRP9LE) +#define AV_PIX_FMT_GBRP10 AV_PIX_FMT_NE(GBRP10BE, GBRP10LE) +#define AV_PIX_FMT_GBRP12 AV_PIX_FMT_NE(GBRP12BE, GBRP12LE) +#define AV_PIX_FMT_GBRP14 AV_PIX_FMT_NE(GBRP14BE, GBRP14LE) +#define AV_PIX_FMT_GBRP16 AV_PIX_FMT_NE(GBRP16BE, GBRP16LE) +#define AV_PIX_FMT_GBRAP10 AV_PIX_FMT_NE(GBRAP10BE, GBRAP10LE) +#define AV_PIX_FMT_GBRAP12 AV_PIX_FMT_NE(GBRAP12BE, GBRAP12LE) +#define AV_PIX_FMT_GBRAP16 AV_PIX_FMT_NE(GBRAP16BE, GBRAP16LE) + +#define AV_PIX_FMT_BAYER_BGGR16 AV_PIX_FMT_NE(BAYER_BGGR16BE, BAYER_BGGR16LE) +#define AV_PIX_FMT_BAYER_RGGB16 AV_PIX_FMT_NE(BAYER_RGGB16BE, BAYER_RGGB16LE) +#define AV_PIX_FMT_BAYER_GBRG16 AV_PIX_FMT_NE(BAYER_GBRG16BE, BAYER_GBRG16LE) +#define AV_PIX_FMT_BAYER_GRBG16 AV_PIX_FMT_NE(BAYER_GRBG16BE, BAYER_GRBG16LE) + +#define AV_PIX_FMT_GBRPF32 AV_PIX_FMT_NE(GBRPF32BE, GBRPF32LE) +#define AV_PIX_FMT_GBRAPF32 AV_PIX_FMT_NE(GBRAPF32BE, GBRAPF32LE) + +#define AV_PIX_FMT_GRAYF32 AV_PIX_FMT_NE(GRAYF32BE, GRAYF32LE) + +#define AV_PIX_FMT_YUVA420P9 AV_PIX_FMT_NE(YUVA420P9BE , YUVA420P9LE) +#define AV_PIX_FMT_YUVA422P9 AV_PIX_FMT_NE(YUVA422P9BE , YUVA422P9LE) +#define AV_PIX_FMT_YUVA444P9 AV_PIX_FMT_NE(YUVA444P9BE , YUVA444P9LE) +#define AV_PIX_FMT_YUVA420P10 AV_PIX_FMT_NE(YUVA420P10BE, YUVA420P10LE) +#define AV_PIX_FMT_YUVA422P10 AV_PIX_FMT_NE(YUVA422P10BE, YUVA422P10LE) +#define AV_PIX_FMT_YUVA444P10 AV_PIX_FMT_NE(YUVA444P10BE, YUVA444P10LE) +#define AV_PIX_FMT_YUVA420P16 AV_PIX_FMT_NE(YUVA420P16BE, YUVA420P16LE) +#define AV_PIX_FMT_YUVA422P16 AV_PIX_FMT_NE(YUVA422P16BE, YUVA422P16LE) +#define AV_PIX_FMT_YUVA444P16 AV_PIX_FMT_NE(YUVA444P16BE, YUVA444P16LE) + +#define AV_PIX_FMT_XYZ12 AV_PIX_FMT_NE(XYZ12BE, XYZ12LE) +#define AV_PIX_FMT_NV20 AV_PIX_FMT_NE(NV20BE, NV20LE) +#define AV_PIX_FMT_AYUV64 AV_PIX_FMT_NE(AYUV64BE, AYUV64LE) +#define AV_PIX_FMT_P010 AV_PIX_FMT_NE(P010BE, P010LE) +#define AV_PIX_FMT_P016 AV_PIX_FMT_NE(P016BE, P016LE) + +/** + * Chromaticity coordinates of the source primaries. + * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.1. + */ +enum AVColorPrimaries { + AVCOL_PRI_RESERVED0 = 0, + AVCOL_PRI_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B + AVCOL_PRI_UNSPECIFIED = 2, + AVCOL_PRI_RESERVED = 3, + AVCOL_PRI_BT470M = 4, ///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20) + + AVCOL_PRI_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM + AVCOL_PRI_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC + AVCOL_PRI_SMPTE240M = 7, ///< functionally identical to above + AVCOL_PRI_FILM = 8, ///< colour filters using Illuminant C + AVCOL_PRI_BT2020 = 9, ///< ITU-R BT2020 + AVCOL_PRI_SMPTE428 = 10, ///< SMPTE ST 428-1 (CIE 1931 XYZ) + AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428, + AVCOL_PRI_SMPTE431 = 11, ///< SMPTE ST 431-2 (2011) / DCI P3 + AVCOL_PRI_SMPTE432 = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3 + AVCOL_PRI_JEDEC_P22 = 22, ///< JEDEC P22 phosphors + AVCOL_PRI_NB ///< Not part of ABI +}; + +/** + * Color Transfer Characteristic. + * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.2. + */ +enum AVColorTransferCharacteristic { + AVCOL_TRC_RESERVED0 = 0, + AVCOL_TRC_BT709 = 1, ///< also ITU-R BT1361 + AVCOL_TRC_UNSPECIFIED = 2, + AVCOL_TRC_RESERVED = 3, + AVCOL_TRC_GAMMA22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM + AVCOL_TRC_GAMMA28 = 5, ///< also ITU-R BT470BG + AVCOL_TRC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC + AVCOL_TRC_SMPTE240M = 7, + AVCOL_TRC_LINEAR = 8, ///< "Linear transfer characteristics" + AVCOL_TRC_LOG = 9, ///< "Logarithmic transfer characteristic (100:1 range)" + AVCOL_TRC_LOG_SQRT = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)" + AVCOL_TRC_IEC61966_2_4 = 11, ///< IEC 61966-2-4 + AVCOL_TRC_BT1361_ECG = 12, ///< ITU-R BT1361 Extended Colour Gamut + AVCOL_TRC_IEC61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC) + AVCOL_TRC_BT2020_10 = 14, ///< ITU-R BT2020 for 10-bit system + AVCOL_TRC_BT2020_12 = 15, ///< ITU-R BT2020 for 12-bit system + AVCOL_TRC_SMPTE2084 = 16, ///< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems + AVCOL_TRC_SMPTEST2084 = AVCOL_TRC_SMPTE2084, + AVCOL_TRC_SMPTE428 = 17, ///< SMPTE ST 428-1 + AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428, + AVCOL_TRC_ARIB_STD_B67 = 18, ///< ARIB STD-B67, known as "Hybrid log-gamma" + AVCOL_TRC_NB ///< Not part of ABI +}; + +/** + * YUV colorspace type. + * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.3. + */ +enum AVColorSpace { + AVCOL_SPC_RGB = 0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB) + AVCOL_SPC_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B + AVCOL_SPC_UNSPECIFIED = 2, + AVCOL_SPC_RESERVED = 3, + AVCOL_SPC_FCC = 4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20) + AVCOL_SPC_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601 + AVCOL_SPC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC + AVCOL_SPC_SMPTE240M = 7, ///< functionally identical to above + AVCOL_SPC_YCGCO = 8, ///< Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16 + AVCOL_SPC_YCOCG = AVCOL_SPC_YCGCO, + AVCOL_SPC_BT2020_NCL = 9, ///< ITU-R BT2020 non-constant luminance system + AVCOL_SPC_BT2020_CL = 10, ///< ITU-R BT2020 constant luminance system + AVCOL_SPC_SMPTE2085 = 11, ///< SMPTE 2085, Y'D'zD'x + AVCOL_SPC_CHROMA_DERIVED_NCL = 12, ///< Chromaticity-derived non-constant luminance system + AVCOL_SPC_CHROMA_DERIVED_CL = 13, ///< Chromaticity-derived constant luminance system + AVCOL_SPC_ICTCP = 14, ///< ITU-R BT.2100-0, ICtCp + AVCOL_SPC_NB ///< Not part of ABI +}; + +/** + * MPEG vs JPEG YUV range. + */ +enum AVColorRange { + AVCOL_RANGE_UNSPECIFIED = 0, + AVCOL_RANGE_MPEG = 1, ///< the normal 219*2^(n-8) "MPEG" YUV ranges + AVCOL_RANGE_JPEG = 2, ///< the normal 2^n-1 "JPEG" YUV ranges + AVCOL_RANGE_NB ///< Not part of ABI +}; + +/** + * Location of chroma samples. + * + * Illustration showing the location of the first (top left) chroma sample of the + * image, the left shows only luma, the right + * shows the location of the chroma sample, the 2 could be imagined to overlay + * each other but are drawn separately due to limitations of ASCII + * + * 1st 2nd 1st 2nd horizontal luma sample positions + * v v v v + * ______ ______ + *1st luma line > |X X ... |3 4 X ... X are luma samples, + * | |1 2 1-6 are possible chroma positions + *2nd luma line > |X X ... |5 6 X ... 0 is undefined/unknown position + */ +enum AVChromaLocation { + AVCHROMA_LOC_UNSPECIFIED = 0, + AVCHROMA_LOC_LEFT = 1, ///< MPEG-2/4 4:2:0, H.264 default for 4:2:0 + AVCHROMA_LOC_CENTER = 2, ///< MPEG-1 4:2:0, JPEG 4:2:0, H.263 4:2:0 + AVCHROMA_LOC_TOPLEFT = 3, ///< ITU-R 601, SMPTE 274M 296M S314M(DV 4:1:1), mpeg2 4:2:2 + AVCHROMA_LOC_TOP = 4, + AVCHROMA_LOC_BOTTOMLEFT = 5, + AVCHROMA_LOC_BOTTOM = 6, + AVCHROMA_LOC_NB ///< Not part of ABI +}; + +#endif /* AVUTIL_PIXFMT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/random_seed.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/random_seed.h new file mode 100644 index 0000000..0462a04 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/random_seed.h
@@ -0,0 +1,43 @@ +/* + * Copyright (c) 2009 Baptiste Coudurier <baptiste.coudurier@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_RANDOM_SEED_H +#define AVUTIL_RANDOM_SEED_H + +#include <stdint.h> +/** + * @addtogroup lavu_crypto + * @{ + */ + +/** + * Get a seed to use in conjunction with random functions. + * This function tries to provide a good seed at a best effort bases. + * Its possible to call this function multiple times if more bits are needed. + * It can be quite slow, which is why it should only be used as seed for a faster + * PRNG. The quality of the seed depends on the platform. + */ +uint32_t av_get_random_seed(void); + +/** + * @} + */ + +#endif /* AVUTIL_RANDOM_SEED_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rational.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rational.h new file mode 100644 index 0000000..5c6b67b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rational.h
@@ -0,0 +1,214 @@ +/* + * rational numbers + * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_math_rational + * Utilties for rational number calculation. + * @author Michael Niedermayer <michaelni@gmx.at> + */ + +#ifndef AVUTIL_RATIONAL_H +#define AVUTIL_RATIONAL_H + +#include <stdint.h> +#include <limits.h> +#include "attributes.h" + +/** + * @defgroup lavu_math_rational AVRational + * @ingroup lavu_math + * Rational number calculation. + * + * While rational numbers can be expressed as floating-point numbers, the + * conversion process is a lossy one, so are floating-point operations. On the + * other hand, the nature of FFmpeg demands highly accurate calculation of + * timestamps. This set of rational number utilities serves as a generic + * interface for manipulating rational numbers as pairs of numerators and + * denominators. + * + * Many of the functions that operate on AVRational's have the suffix `_q`, in + * reference to the mathematical symbol "ℚ" (Q) which denotes the set of all + * rational numbers. + * + * @{ + */ + +/** + * Rational number (pair of numerator and denominator). + */ +typedef struct AVRational{ + int num; ///< Numerator + int den; ///< Denominator +} AVRational; + +/** + * Create an AVRational. + * + * Useful for compilers that do not support compound literals. + * + * @note The return value is not reduced. + * @see av_reduce() + */ +static inline AVRational av_make_q(int num, int den) +{ + AVRational r = { num, den }; + return r; +} + +/** + * Compare two rationals. + * + * @param a First rational + * @param b Second rational + * + * @return One of the following values: + * - 0 if `a == b` + * - 1 if `a > b` + * - -1 if `a < b` + * - `INT_MIN` if one of the values is of the form `0 / 0` + */ +static inline int av_cmp_q(AVRational a, AVRational b){ + const int64_t tmp= a.num * (int64_t)b.den - b.num * (int64_t)a.den; + + if(tmp) return (int)((tmp ^ a.den ^ b.den)>>63)|1; + else if(b.den && a.den) return 0; + else if(a.num && b.num) return (a.num>>31) - (b.num>>31); + else return INT_MIN; +} + +/** + * Convert an AVRational to a `double`. + * @param a AVRational to convert + * @return `a` in floating-point form + * @see av_d2q() + */ +static inline double av_q2d(AVRational a){ + return a.num / (double) a.den; +} + +/** + * Reduce a fraction. + * + * This is useful for framerate calculations. + * + * @param[out] dst_num Destination numerator + * @param[out] dst_den Destination denominator + * @param[in] num Source numerator + * @param[in] den Source denominator + * @param[in] max Maximum allowed values for `dst_num` & `dst_den` + * @return 1 if the operation is exact, 0 otherwise + */ +int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max); + +/** + * Multiply two rationals. + * @param b First rational + * @param c Second rational + * @return b*c + */ +AVRational av_mul_q(AVRational b, AVRational c) av_const; + +/** + * Divide one rational by another. + * @param b First rational + * @param c Second rational + * @return b/c + */ +AVRational av_div_q(AVRational b, AVRational c) av_const; + +/** + * Add two rationals. + * @param b First rational + * @param c Second rational + * @return b+c + */ +AVRational av_add_q(AVRational b, AVRational c) av_const; + +/** + * Subtract one rational from another. + * @param b First rational + * @param c Second rational + * @return b-c + */ +AVRational av_sub_q(AVRational b, AVRational c) av_const; + +/** + * Invert a rational. + * @param q value + * @return 1 / q + */ +static av_always_inline AVRational av_inv_q(AVRational q) +{ + AVRational r = { q.den, q.num }; + return r; +} + +/** + * Convert a double precision floating point number to a rational. + * + * In case of infinity, the returned value is expressed as `{1, 0}` or + * `{-1, 0}` depending on the sign. + * + * @param d `double` to convert + * @param max Maximum allowed numerator and denominator + * @return `d` in AVRational form + * @see av_q2d() + */ +AVRational av_d2q(double d, int max) av_const; + +/** + * Find which of the two rationals is closer to another rational. + * + * @param q Rational to be compared against + * @param q1,q2 Rationals to be tested + * @return One of the following values: + * - 1 if `q1` is nearer to `q` than `q2` + * - -1 if `q2` is nearer to `q` than `q1` + * - 0 if they have the same distance + */ +int av_nearer_q(AVRational q, AVRational q1, AVRational q2); + +/** + * Find the value in a list of rationals nearest a given reference rational. + * + * @param q Reference rational + * @param q_list Array of rationals terminated by `{0, 0}` + * @return Index of the nearest value found in the array + */ +int av_find_nearest_q_idx(AVRational q, const AVRational* q_list); + +/** + * Convert an AVRational to a IEEE 32-bit `float` expressed in fixed-point + * format. + * + * @param q Rational to be converted + * @return Equivalent floating-point value, expressed as an unsigned 32-bit + * integer. + * @note The returned value is platform-indepedant. + */ +uint32_t av_q2intfloat(AVRational q); + +/** + * @} + */ + +#endif /* AVUTIL_RATIONAL_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rc4.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rc4.h new file mode 100644 index 0000000..029cd2a --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rc4.h
@@ -0,0 +1,66 @@ +/* + * RC4 encryption/decryption/pseudo-random number generator + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_RC4_H +#define AVUTIL_RC4_H + +#include <stdint.h> + +/** + * @defgroup lavu_rc4 RC4 + * @ingroup lavu_crypto + * @{ + */ + +typedef struct AVRC4 { + uint8_t state[256]; + int x, y; +} AVRC4; + +/** + * Allocate an AVRC4 context. + */ +AVRC4 *av_rc4_alloc(void); + +/** + * @brief Initializes an AVRC4 context. + * + * @param key_bits must be a multiple of 8 + * @param decrypt 0 for encryption, 1 for decryption, currently has no effect + * @return zero on success, negative value otherwise + */ +int av_rc4_init(struct AVRC4 *d, const uint8_t *key, int key_bits, int decrypt); + +/** + * @brief Encrypts / decrypts using the RC4 algorithm. + * + * @param count number of bytes + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst, may be NULL + * @param iv not (yet) used for RC4, should be NULL + * @param decrypt 0 for encryption, 1 for decryption, not (yet) used + */ +void av_rc4_crypt(struct AVRC4 *d, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_RC4_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/replaygain.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/replaygain.h new file mode 100644 index 0000000..b49bf1a --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/replaygain.h
@@ -0,0 +1,50 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_REPLAYGAIN_H +#define AVUTIL_REPLAYGAIN_H + +#include <stdint.h> + +/** + * ReplayGain information (see + * http://wiki.hydrogenaudio.org/index.php?title=ReplayGain_1.0_specification). + * The size of this struct is a part of the public ABI. + */ +typedef struct AVReplayGain { + /** + * Track replay gain in microbels (divide by 100000 to get the value in dB). + * Should be set to INT32_MIN when unknown. + */ + int32_t track_gain; + /** + * Peak track amplitude, with 100000 representing full scale (but values + * may overflow). 0 when unknown. + */ + uint32_t track_peak; + /** + * Same as track_gain, but for the whole album. + */ + int32_t album_gain; + /** + * Same as track_peak, but for the whole album, + */ + uint32_t album_peak; +} AVReplayGain; + +#endif /* AVUTIL_REPLAYGAIN_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ripemd.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ripemd.h new file mode 100644 index 0000000..0db6858 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ripemd.h
@@ -0,0 +1,87 @@ +/* + * Copyright (C) 2007 Michael Niedermayer <michaelni@gmx.at> + * Copyright (C) 2013 James Almer <jamrial@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_ripemd + * Public header for RIPEMD hash function implementation. + */ + +#ifndef AVUTIL_RIPEMD_H +#define AVUTIL_RIPEMD_H + +#include <stdint.h> + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_ripemd RIPEMD + * @ingroup lavu_hash + * RIPEMD hash function implementation. + * + * @{ + */ + +extern const int av_ripemd_size; + +struct AVRIPEMD; + +/** + * Allocate an AVRIPEMD context. + */ +struct AVRIPEMD *av_ripemd_alloc(void); + +/** + * Initialize RIPEMD hashing. + * + * @param context pointer to the function context (of size av_ripemd_size) + * @param bits number of bits in digest (128, 160, 256 or 320 bits) + * @return zero if initialization succeeded, -1 otherwise + */ +int av_ripemd_init(struct AVRIPEMD* context, int bits); + +/** + * Update hash value. + * + * @param context hash function context + * @param data input data to update hash with + * @param len input data length + */ +#if FF_API_CRYPTO_SIZE_T +void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, unsigned int len); +#else +void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param context hash function context + * @param digest buffer where output digest value is stored + */ +void av_ripemd_final(struct AVRIPEMD* context, uint8_t *digest); + +/** + * @} + */ + +#endif /* AVUTIL_RIPEMD_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/samplefmt.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/samplefmt.h new file mode 100644 index 0000000..8cd43ae --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/samplefmt.h
@@ -0,0 +1,272 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_SAMPLEFMT_H +#define AVUTIL_SAMPLEFMT_H + +#include <stdint.h> + +#include "avutil.h" +#include "attributes.h" + +/** + * @addtogroup lavu_audio + * @{ + * + * @defgroup lavu_sampfmts Audio sample formats + * + * Audio sample format enumeration and related convenience functions. + * @{ + */ + +/** + * Audio sample formats + * + * - The data described by the sample format is always in native-endian order. + * Sample values can be expressed by native C types, hence the lack of a signed + * 24-bit sample format even though it is a common raw audio data format. + * + * - The floating-point formats are based on full volume being in the range + * [-1.0, 1.0]. Any values outside this range are beyond full volume level. + * + * - The data layout as used in av_samples_fill_arrays() and elsewhere in FFmpeg + * (such as AVFrame in libavcodec) is as follows: + * + * @par + * For planar sample formats, each audio channel is in a separate data plane, + * and linesize is the buffer size, in bytes, for a single plane. All data + * planes must be the same size. For packed sample formats, only the first data + * plane is used, and samples for each channel are interleaved. In this case, + * linesize is the buffer size, in bytes, for the 1 plane. + * + */ +enum AVSampleFormat { + AV_SAMPLE_FMT_NONE = -1, + AV_SAMPLE_FMT_U8, ///< unsigned 8 bits + AV_SAMPLE_FMT_S16, ///< signed 16 bits + AV_SAMPLE_FMT_S32, ///< signed 32 bits + AV_SAMPLE_FMT_FLT, ///< float + AV_SAMPLE_FMT_DBL, ///< double + + AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar + AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar + AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar + AV_SAMPLE_FMT_FLTP, ///< float, planar + AV_SAMPLE_FMT_DBLP, ///< double, planar + AV_SAMPLE_FMT_S64, ///< signed 64 bits + AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar + + AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically +}; + +/** + * Return the name of sample_fmt, or NULL if sample_fmt is not + * recognized. + */ +const char *av_get_sample_fmt_name(enum AVSampleFormat sample_fmt); + +/** + * Return a sample format corresponding to name, or AV_SAMPLE_FMT_NONE + * on error. + */ +enum AVSampleFormat av_get_sample_fmt(const char *name); + +/** + * Return the planar<->packed alternative form of the given sample format, or + * AV_SAMPLE_FMT_NONE on error. If the passed sample_fmt is already in the + * requested planar/packed format, the format returned is the same as the + * input. + */ +enum AVSampleFormat av_get_alt_sample_fmt(enum AVSampleFormat sample_fmt, int planar); + +/** + * Get the packed alternative form of the given sample format. + * + * If the passed sample_fmt is already in packed format, the format returned is + * the same as the input. + * + * @return the packed alternative form of the given sample format or + AV_SAMPLE_FMT_NONE on error. + */ +enum AVSampleFormat av_get_packed_sample_fmt(enum AVSampleFormat sample_fmt); + +/** + * Get the planar alternative form of the given sample format. + * + * If the passed sample_fmt is already in planar format, the format returned is + * the same as the input. + * + * @return the planar alternative form of the given sample format or + AV_SAMPLE_FMT_NONE on error. + */ +enum AVSampleFormat av_get_planar_sample_fmt(enum AVSampleFormat sample_fmt); + +/** + * Generate a string corresponding to the sample format with + * sample_fmt, or a header if sample_fmt is negative. + * + * @param buf the buffer where to write the string + * @param buf_size the size of buf + * @param sample_fmt the number of the sample format to print the + * corresponding info string, or a negative value to print the + * corresponding header. + * @return the pointer to the filled buffer or NULL if sample_fmt is + * unknown or in case of other errors + */ +char *av_get_sample_fmt_string(char *buf, int buf_size, enum AVSampleFormat sample_fmt); + +/** + * Return number of bytes per sample. + * + * @param sample_fmt the sample format + * @return number of bytes per sample or zero if unknown for the given + * sample format + */ +int av_get_bytes_per_sample(enum AVSampleFormat sample_fmt); + +/** + * Check if the sample format is planar. + * + * @param sample_fmt the sample format to inspect + * @return 1 if the sample format is planar, 0 if it is interleaved + */ +int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt); + +/** + * Get the required buffer size for the given audio parameters. + * + * @param[out] linesize calculated linesize, may be NULL + * @param nb_channels the number of channels + * @param nb_samples the number of samples in a single channel + * @param sample_fmt the sample format + * @param align buffer size alignment (0 = default, 1 = no alignment) + * @return required buffer size, or negative error code on failure + */ +int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples, + enum AVSampleFormat sample_fmt, int align); + +/** + * @} + * + * @defgroup lavu_sampmanip Samples manipulation + * + * Functions that manipulate audio samples + * @{ + */ + +/** + * Fill plane data pointers and linesize for samples with sample + * format sample_fmt. + * + * The audio_data array is filled with the pointers to the samples data planes: + * for planar, set the start point of each channel's data within the buffer, + * for packed, set the start point of the entire buffer only. + * + * The value pointed to by linesize is set to the aligned size of each + * channel's data buffer for planar layout, or to the aligned size of the + * buffer for all channels for packed layout. + * + * The buffer in buf must be big enough to contain all the samples + * (use av_samples_get_buffer_size() to compute its minimum size), + * otherwise the audio_data pointers will point to invalid data. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param[out] audio_data array to be filled with the pointer for each channel + * @param[out] linesize calculated linesize, may be NULL + * @param buf the pointer to a buffer containing the samples + * @param nb_channels the number of channels + * @param nb_samples the number of samples in a single channel + * @param sample_fmt the sample format + * @param align buffer size alignment (0 = default, 1 = no alignment) + * @return >=0 on success or a negative error code on failure + * @todo return minimum size in bytes required for the buffer in case + * of success at the next bump + */ +int av_samples_fill_arrays(uint8_t **audio_data, int *linesize, + const uint8_t *buf, + int nb_channels, int nb_samples, + enum AVSampleFormat sample_fmt, int align); + +/** + * Allocate a samples buffer for nb_samples samples, and fill data pointers and + * linesize accordingly. + * The allocated samples buffer can be freed by using av_freep(&audio_data[0]) + * Allocated data will be initialized to silence. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param[out] audio_data array to be filled with the pointer for each channel + * @param[out] linesize aligned size for audio buffer(s), may be NULL + * @param nb_channels number of audio channels + * @param nb_samples number of samples per channel + * @param align buffer size alignment (0 = default, 1 = no alignment) + * @return >=0 on success or a negative error code on failure + * @todo return the size of the allocated buffer in case of success at the next bump + * @see av_samples_fill_arrays() + * @see av_samples_alloc_array_and_samples() + */ +int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels, + int nb_samples, enum AVSampleFormat sample_fmt, int align); + +/** + * Allocate a data pointers array, samples buffer for nb_samples + * samples, and fill data pointers and linesize accordingly. + * + * This is the same as av_samples_alloc(), but also allocates the data + * pointers array. + * + * @see av_samples_alloc() + */ +int av_samples_alloc_array_and_samples(uint8_t ***audio_data, int *linesize, int nb_channels, + int nb_samples, enum AVSampleFormat sample_fmt, int align); + +/** + * Copy samples from src to dst. + * + * @param dst destination array of pointers to data planes + * @param src source array of pointers to data planes + * @param dst_offset offset in samples at which the data will be written to dst + * @param src_offset offset in samples at which the data will be read from src + * @param nb_samples number of samples to be copied + * @param nb_channels number of audio channels + * @param sample_fmt audio sample format + */ +int av_samples_copy(uint8_t **dst, uint8_t * const *src, int dst_offset, + int src_offset, int nb_samples, int nb_channels, + enum AVSampleFormat sample_fmt); + +/** + * Fill an audio buffer with silence. + * + * @param audio_data array of pointers to data planes + * @param offset offset in samples at which to start filling + * @param nb_samples number of samples to fill + * @param nb_channels number of audio channels + * @param sample_fmt audio sample format + */ +int av_samples_set_silence(uint8_t **audio_data, int offset, int nb_samples, + int nb_channels, enum AVSampleFormat sample_fmt); + +/** + * @} + * @} + */ +#endif /* AVUTIL_SAMPLEFMT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha.h new file mode 100644 index 0000000..c0180e5 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha.h
@@ -0,0 +1,95 @@ +/* + * Copyright (C) 2007 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_sha + * Public header for SHA-1 & SHA-256 hash function implementations. + */ + +#ifndef AVUTIL_SHA_H +#define AVUTIL_SHA_H + +#include <stddef.h> +#include <stdint.h> + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_sha SHA + * @ingroup lavu_hash + * SHA-1 and SHA-256 (Secure Hash Algorithm) hash function implementations. + * + * This module supports the following SHA hash functions: + * + * - SHA-1: 160 bits + * - SHA-224: 224 bits, as a variant of SHA-2 + * - SHA-256: 256 bits, as a variant of SHA-2 + * + * @see For SHA-384, SHA-512, and variants thereof, see @ref lavu_sha512. + * + * @{ + */ + +extern const int av_sha_size; + +struct AVSHA; + +/** + * Allocate an AVSHA context. + */ +struct AVSHA *av_sha_alloc(void); + +/** + * Initialize SHA-1 or SHA-2 hashing. + * + * @param context pointer to the function context (of size av_sha_size) + * @param bits number of bits in digest (SHA-1 - 160 bits, SHA-2 224 or 256 bits) + * @return zero if initialization succeeded, -1 otherwise + */ +int av_sha_init(struct AVSHA* context, int bits); + +/** + * Update hash value. + * + * @param ctx hash function context + * @param data input data to update hash with + * @param len input data length + */ +#if FF_API_CRYPTO_SIZE_T +void av_sha_update(struct AVSHA *ctx, const uint8_t *data, unsigned int len); +#else +void av_sha_update(struct AVSHA *ctx, const uint8_t *data, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param context hash function context + * @param digest buffer where output digest value is stored + */ +void av_sha_final(struct AVSHA* context, uint8_t *digest); + +/** + * @} + */ + +#endif /* AVUTIL_SHA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha512.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha512.h new file mode 100644 index 0000000..bef714b --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha512.h
@@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007 Michael Niedermayer <michaelni@gmx.at> + * Copyright (C) 2013 James Almer <jamrial@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_sha512 + * Public header for SHA-512 implementation. + */ + +#ifndef AVUTIL_SHA512_H +#define AVUTIL_SHA512_H + +#include <stddef.h> +#include <stdint.h> + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_sha512 SHA-512 + * @ingroup lavu_hash + * SHA-512 (Secure Hash Algorithm) hash function implementations. + * + * This module supports the following SHA-2 hash functions: + * + * - SHA-512/224: 224 bits + * - SHA-512/256: 256 bits + * - SHA-384: 384 bits + * - SHA-512: 512 bits + * + * @see For SHA-1, SHA-256, and variants thereof, see @ref lavu_sha. + * + * @{ + */ + +extern const int av_sha512_size; + +struct AVSHA512; + +/** + * Allocate an AVSHA512 context. + */ +struct AVSHA512 *av_sha512_alloc(void); + +/** + * Initialize SHA-2 512 hashing. + * + * @param context pointer to the function context (of size av_sha512_size) + * @param bits number of bits in digest (224, 256, 384 or 512 bits) + * @return zero if initialization succeeded, -1 otherwise + */ +int av_sha512_init(struct AVSHA512* context, int bits); + +/** + * Update hash value. + * + * @param context hash function context + * @param data input data to update hash with + * @param len input data length + */ +#if FF_API_CRYPTO_SIZE_T +void av_sha512_update(struct AVSHA512* context, const uint8_t* data, unsigned int len); +#else +void av_sha512_update(struct AVSHA512* context, const uint8_t* data, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param context hash function context + * @param digest buffer where output digest value is stored + */ +void av_sha512_final(struct AVSHA512* context, uint8_t *digest); + +/** + * @} + */ + +#endif /* AVUTIL_SHA512_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/spherical.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/spherical.h new file mode 100644 index 0000000..cef759c --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/spherical.h
@@ -0,0 +1,232 @@ +/* + * Copyright (c) 2016 Vittorio Giovara <vittorio.giovara@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Spherical video + */ + +#ifndef AVUTIL_SPHERICAL_H +#define AVUTIL_SPHERICAL_H + +#include <stddef.h> +#include <stdint.h> + +/** + * @addtogroup lavu_video + * @{ + * + * @defgroup lavu_video_spherical Spherical video mapping + * @{ + */ + +/** + * @addtogroup lavu_video_spherical + * A spherical video file contains surfaces that need to be mapped onto a + * sphere. Depending on how the frame was converted, a different distortion + * transformation or surface recomposition function needs to be applied before + * the video should be mapped and displayed. + */ + +/** + * Projection of the video surface(s) on a sphere. + */ +enum AVSphericalProjection { + /** + * Video represents a sphere mapped on a flat surface using + * equirectangular projection. + */ + AV_SPHERICAL_EQUIRECTANGULAR, + + /** + * Video frame is split into 6 faces of a cube, and arranged on a + * 3x2 layout. Faces are oriented upwards for the front, left, right, + * and back faces. The up face is oriented so the top of the face is + * forwards and the down face is oriented so the top of the face is + * to the back. + */ + AV_SPHERICAL_CUBEMAP, + + /** + * Video represents a portion of a sphere mapped on a flat surface + * using equirectangular projection. The @ref bounding fields indicate + * the position of the current video in a larger surface. + */ + AV_SPHERICAL_EQUIRECTANGULAR_TILE, +}; + +/** + * This structure describes how to handle spherical videos, outlining + * information about projection, initial layout, and any other view modifier. + * + * @note The struct must be allocated with av_spherical_alloc() and + * its size is not a part of the public ABI. + */ +typedef struct AVSphericalMapping { + /** + * Projection type. + */ + enum AVSphericalProjection projection; + + /** + * @name Initial orientation + * @{ + * There fields describe additional rotations applied to the sphere after + * the video frame is mapped onto it. The sphere is rotated around the + * viewer, who remains stationary. The order of transformation is always + * yaw, followed by pitch, and finally by roll. + * + * The coordinate system matches the one defined in OpenGL, where the + * forward vector (z) is coming out of screen, and it is equivalent to + * a rotation matrix of R = r_y(yaw) * r_x(pitch) * r_z(roll). + * + * A positive yaw rotates the portion of the sphere in front of the viewer + * toward their right. A positive pitch rotates the portion of the sphere + * in front of the viewer upwards. A positive roll tilts the portion of + * the sphere in front of the viewer to the viewer's right. + * + * These values are exported as 16.16 fixed point. + * + * See this equirectangular projection as example: + * + * @code{.unparsed} + * Yaw + * -180 0 180 + * 90 +-------------+-------------+ 180 + * | | | up + * P | | | y| forward + * i | ^ | | /z + * t 0 +-------------X-------------+ 0 Roll | / + * c | | | | / + * h | | | 0|/_____right + * | | | x + * -90 +-------------+-------------+ -180 + * + * X - the default camera center + * ^ - the default up vector + * @endcode + */ + int32_t yaw; ///< Rotation around the up vector [-180, 180]. + int32_t pitch; ///< Rotation around the right vector [-90, 90]. + int32_t roll; ///< Rotation around the forward vector [-180, 180]. + /** + * @} + */ + + /** + * @name Bounding rectangle + * @anchor bounding + * @{ + * These fields indicate the location of the current tile, and where + * it should be mapped relative to the original surface. They are + * exported as 0.32 fixed point, and can be converted to classic + * pixel values with av_spherical_bounds(). + * + * @code{.unparsed} + * +----------------+----------+ + * | |bound_top | + * | +--------+ | + * | bound_left |tile | | + * +<---------->| |<--->+bound_right + * | +--------+ | + * | | | + * | bound_bottom| | + * +----------------+----------+ + * @endcode + * + * If needed, the original video surface dimensions can be derived + * by adding the current stream or frame size to the related bounds, + * like in the following example: + * + * @code{c} + * original_width = tile->width + bound_left + bound_right; + * original_height = tile->height + bound_top + bound_bottom; + * @endcode + * + * @note These values are valid only for the tiled equirectangular + * projection type (@ref AV_SPHERICAL_EQUIRECTANGULAR_TILE), + * and should be ignored in all other cases. + */ + uint32_t bound_left; ///< Distance from the left edge + uint32_t bound_top; ///< Distance from the top edge + uint32_t bound_right; ///< Distance from the right edge + uint32_t bound_bottom; ///< Distance from the bottom edge + /** + * @} + */ + + /** + * Number of pixels to pad from the edge of each cube face. + * + * @note This value is valid for only for the cubemap projection type + * (@ref AV_SPHERICAL_CUBEMAP), and should be ignored in all other + * cases. + */ + uint32_t padding; +} AVSphericalMapping; + +/** + * Allocate a AVSphericalVideo structure and initialize its fields to default + * values. + * + * @return the newly allocated struct or NULL on failure + */ +AVSphericalMapping *av_spherical_alloc(size_t *size); + +/** + * Convert the @ref bounding fields from an AVSphericalVideo + * from 0.32 fixed point to pixels. + * + * @param map The AVSphericalVideo map to read bound values from. + * @param width Width of the current frame or stream. + * @param height Height of the current frame or stream. + * @param left Pixels from the left edge. + * @param top Pixels from the top edge. + * @param right Pixels from the right edge. + * @param bottom Pixels from the bottom edge. + */ +void av_spherical_tile_bounds(const AVSphericalMapping *map, + size_t width, size_t height, + size_t *left, size_t *top, + size_t *right, size_t *bottom); + +/** + * Provide a human-readable name of a given AVSphericalProjection. + * + * @param projection The input AVSphericalProjection. + * + * @return The name of the AVSphericalProjection, or "unknown". + */ +const char *av_spherical_projection_name(enum AVSphericalProjection projection); + +/** + * Get the AVSphericalProjection form a human-readable name. + * + * @param name The input string. + * + * @return The AVSphericalProjection value, or -1 if not found. + */ +int av_spherical_from_name(const char *name); +/** + * @} + * @} + */ + +#endif /* AVUTIL_SPHERICAL_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/stereo3d.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/stereo3d.h new file mode 100644 index 0000000..d421aac --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/stereo3d.h
@@ -0,0 +1,233 @@ +/* + * Copyright (c) 2013 Vittorio Giovara <vittorio.giovara@gmail.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Stereoscopic video + */ + +#ifndef AVUTIL_STEREO3D_H +#define AVUTIL_STEREO3D_H + +#include <stdint.h> + +#include "frame.h" + +/** + * @addtogroup lavu_video + * @{ + * + * @defgroup lavu_video_stereo3d Stereo3D types and functions + * @{ + */ + +/** + * @addtogroup lavu_video_stereo3d + * A stereoscopic video file consists in multiple views embedded in a single + * frame, usually describing two views of a scene. This file describes all + * possible codec-independent view arrangements. + * */ + +/** + * List of possible 3D Types + */ +enum AVStereo3DType { + /** + * Video is not stereoscopic (and metadata has to be there). + */ + AV_STEREO3D_2D, + + /** + * Views are next to each other. + * + * @code{.unparsed} + * LLLLRRRR + * LLLLRRRR + * LLLLRRRR + * ... + * @endcode + */ + AV_STEREO3D_SIDEBYSIDE, + + /** + * Views are on top of each other. + * + * @code{.unparsed} + * LLLLLLLL + * LLLLLLLL + * RRRRRRRR + * RRRRRRRR + * @endcode + */ + AV_STEREO3D_TOPBOTTOM, + + /** + * Views are alternated temporally. + * + * @code{.unparsed} + * frame0 frame1 frame2 ... + * LLLLLLLL RRRRRRRR LLLLLLLL + * LLLLLLLL RRRRRRRR LLLLLLLL + * LLLLLLLL RRRRRRRR LLLLLLLL + * ... ... ... + * @endcode + */ + AV_STEREO3D_FRAMESEQUENCE, + + /** + * Views are packed in a checkerboard-like structure per pixel. + * + * @code{.unparsed} + * LRLRLRLR + * RLRLRLRL + * LRLRLRLR + * ... + * @endcode + */ + AV_STEREO3D_CHECKERBOARD, + + /** + * Views are next to each other, but when upscaling + * apply a checkerboard pattern. + * + * @code{.unparsed} + * LLLLRRRR L L L L R R R R + * LLLLRRRR => L L L L R R R R + * LLLLRRRR L L L L R R R R + * LLLLRRRR L L L L R R R R + * @endcode + */ + AV_STEREO3D_SIDEBYSIDE_QUINCUNX, + + /** + * Views are packed per line, as if interlaced. + * + * @code{.unparsed} + * LLLLLLLL + * RRRRRRRR + * LLLLLLLL + * ... + * @endcode + */ + AV_STEREO3D_LINES, + + /** + * Views are packed per column. + * + * @code{.unparsed} + * LRLRLRLR + * LRLRLRLR + * LRLRLRLR + * ... + * @endcode + */ + AV_STEREO3D_COLUMNS, +}; + +/** + * List of possible view types. + */ +enum AVStereo3DView { + /** + * Frame contains two packed views. + */ + AV_STEREO3D_VIEW_PACKED, + + /** + * Frame contains only the left view. + */ + AV_STEREO3D_VIEW_LEFT, + + /** + * Frame contains only the right view. + */ + AV_STEREO3D_VIEW_RIGHT, +}; + +/** + * Inverted views, Right/Bottom represents the left view. + */ +#define AV_STEREO3D_FLAG_INVERT (1 << 0) + +/** + * Stereo 3D type: this structure describes how two videos are packed + * within a single video surface, with additional information as needed. + * + * @note The struct must be allocated with av_stereo3d_alloc() and + * its size is not a part of the public ABI. + */ +typedef struct AVStereo3D { + /** + * How views are packed within the video. + */ + enum AVStereo3DType type; + + /** + * Additional information about the frame packing. + */ + int flags; + + /** + * Determines which views are packed. + */ + enum AVStereo3DView view; +} AVStereo3D; + +/** + * Allocate an AVStereo3D structure and set its fields to default values. + * The resulting struct can be freed using av_freep(). + * + * @return An AVStereo3D filled with default values or NULL on failure. + */ +AVStereo3D *av_stereo3d_alloc(void); + +/** + * Allocate a complete AVFrameSideData and add it to the frame. + * + * @param frame The frame which side data is added to. + * + * @return The AVStereo3D structure to be filled by caller. + */ +AVStereo3D *av_stereo3d_create_side_data(AVFrame *frame); + +/** + * Provide a human-readable name of a given stereo3d type. + * + * @param type The input stereo3d type value. + * + * @return The name of the stereo3d value, or "unknown". + */ +const char *av_stereo3d_type_name(unsigned int type); + +/** + * Get the AVStereo3DType form a human-readable name. + * + * @param name The input string. + * + * @return The AVStereo3DType value, or -1 if not found. + */ +int av_stereo3d_from_name(const char *name); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_STEREO3D_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tea.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tea.h new file mode 100644 index 0000000..dd929bd --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tea.h
@@ -0,0 +1,71 @@ +/* + * A 32-bit implementation of the TEA algorithm + * Copyright (c) 2015 Vesselin Bontchev + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_TEA_H +#define AVUTIL_TEA_H + +#include <stdint.h> + +/** + * @file + * @brief Public header for libavutil TEA algorithm + * @defgroup lavu_tea TEA + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_tea_size; + +struct AVTEA; + +/** + * Allocate an AVTEA context + * To free the struct: av_free(ptr) + */ +struct AVTEA *av_tea_alloc(void); + +/** + * Initialize an AVTEA context. + * + * @param ctx an AVTEA context + * @param key a key of 16 bytes used for encryption/decryption + * @param rounds the number of rounds in TEA (64 is the "standard") + */ +void av_tea_init(struct AVTEA *ctx, const uint8_t key[16], int rounds); + +/** + * Encrypt or decrypt a buffer using a previously initialized context. + * + * @param ctx an AVTEA context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, if NULL then ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_tea_crypt(struct AVTEA *ctx, uint8_t *dst, const uint8_t *src, + int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_TEA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/threadmessage.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/threadmessage.h new file mode 100644 index 0000000..42ce655 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/threadmessage.h
@@ -0,0 +1,115 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_THREADMESSAGE_H +#define AVUTIL_THREADMESSAGE_H + +typedef struct AVThreadMessageQueue AVThreadMessageQueue; + +typedef enum AVThreadMessageFlags { + + /** + * Perform non-blocking operation. + * If this flag is set, send and recv operations are non-blocking and + * return AVERROR(EAGAIN) immediately if they can not proceed. + */ + AV_THREAD_MESSAGE_NONBLOCK = 1, + +} AVThreadMessageFlags; + +/** + * Allocate a new message queue. + * + * @param mq pointer to the message queue + * @param nelem maximum number of elements in the queue + * @param elsize size of each element in the queue + * @return >=0 for success; <0 for error, in particular AVERROR(ENOSYS) if + * lavu was built without thread support + */ +int av_thread_message_queue_alloc(AVThreadMessageQueue **mq, + unsigned nelem, + unsigned elsize); + +/** + * Free a message queue. + * + * The message queue must no longer be in use by another thread. + */ +void av_thread_message_queue_free(AVThreadMessageQueue **mq); + +/** + * Send a message on the queue. + */ +int av_thread_message_queue_send(AVThreadMessageQueue *mq, + void *msg, + unsigned flags); + +/** + * Receive a message from the queue. + */ +int av_thread_message_queue_recv(AVThreadMessageQueue *mq, + void *msg, + unsigned flags); + +/** + * Set the sending error code. + * + * If the error code is set to non-zero, av_thread_message_queue_send() will + * return it immediately. Conventional values, such as AVERROR_EOF or + * AVERROR(EAGAIN), can be used to cause the sending thread to stop or + * suspend its operation. + */ +void av_thread_message_queue_set_err_send(AVThreadMessageQueue *mq, + int err); + +/** + * Set the receiving error code. + * + * If the error code is set to non-zero, av_thread_message_queue_recv() will + * return it immediately when there are no longer available messages. + * Conventional values, such as AVERROR_EOF or AVERROR(EAGAIN), can be used + * to cause the receiving thread to stop or suspend its operation. + */ +void av_thread_message_queue_set_err_recv(AVThreadMessageQueue *mq, + int err); + +/** + * Set the optional free message callback function which will be called if an + * operation is removing messages from the queue. + */ +void av_thread_message_queue_set_free_func(AVThreadMessageQueue *mq, + void (*free_func)(void *msg)); + +/** + * Return the current number of messages in the queue. + * + * @return the current number of messages or AVERROR(ENOSYS) if lavu was built + * without thread support + */ +int av_thread_message_queue_nb_elems(AVThreadMessageQueue *mq); + +/** + * Flush the message queue + * + * This function is mostly equivalent to reading and free-ing every message + * except that it will be done in a single operation (no lock/unlock between + * reads). + */ +void av_thread_message_flush(AVThreadMessageQueue *mq); + +#endif /* AVUTIL_THREADMESSAGE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/time.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/time.h new file mode 100644 index 0000000..dc169b0 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/time.h
@@ -0,0 +1,56 @@ +/* + * Copyright (c) 2000-2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_TIME_H +#define AVUTIL_TIME_H + +#include <stdint.h> + +/** + * Get the current time in microseconds. + */ +int64_t av_gettime(void); + +/** + * Get the current time in microseconds since some unspecified starting point. + * On platforms that support it, the time comes from a monotonic clock + * This property makes this time source ideal for measuring relative time. + * The returned values may not be monotonic on platforms where a monotonic + * clock is not available. + */ +int64_t av_gettime_relative(void); + +/** + * Indicates with a boolean result if the av_gettime_relative() time source + * is monotonic. + */ +int av_gettime_relative_is_monotonic(void); + +/** + * Sleep for a period of time. Although the duration is expressed in + * microseconds, the actual delay may be rounded to the precision of the + * system timer. + * + * @param usec Number of microseconds to sleep. + * @return zero on success or (negative) error code. + */ +int av_usleep(unsigned usec); + +#endif /* AVUTIL_TIME_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timecode.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timecode.h new file mode 100644 index 0000000..37c1361 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timecode.h
@@ -0,0 +1,140 @@ +/* + * Copyright (c) 2006 Smartjog S.A.S, Baptiste Coudurier <baptiste.coudurier@gmail.com> + * Copyright (c) 2011-2012 Smartjog S.A.S, Clément Bœsch <clement.boesch@smartjog.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Timecode helpers header + */ + +#ifndef AVUTIL_TIMECODE_H +#define AVUTIL_TIMECODE_H + +#include <stdint.h> +#include "rational.h" + +#define AV_TIMECODE_STR_SIZE 23 + +enum AVTimecodeFlag { + AV_TIMECODE_FLAG_DROPFRAME = 1<<0, ///< timecode is drop frame + AV_TIMECODE_FLAG_24HOURSMAX = 1<<1, ///< timecode wraps after 24 hours + AV_TIMECODE_FLAG_ALLOWNEGATIVE = 1<<2, ///< negative time values are allowed +}; + +typedef struct { + int start; ///< timecode frame start (first base frame number) + uint32_t flags; ///< flags such as drop frame, +24 hours support, ... + AVRational rate; ///< frame rate in rational form + unsigned fps; ///< frame per second; must be consistent with the rate field +} AVTimecode; + +/** + * Adjust frame number for NTSC drop frame time code. + * + * @param framenum frame number to adjust + * @param fps frame per second, 30 or 60 + * @return adjusted frame number + * @warning adjustment is only valid in NTSC 29.97 and 59.94 + */ +int av_timecode_adjust_ntsc_framenum2(int framenum, int fps); + +/** + * Convert frame number to SMPTE 12M binary representation. + * + * @param tc timecode data correctly initialized + * @param framenum frame number + * @return the SMPTE binary representation + * + * @note Frame number adjustment is automatically done in case of drop timecode, + * you do NOT have to call av_timecode_adjust_ntsc_framenum2(). + * @note The frame number is relative to tc->start. + * @note Color frame (CF), binary group flags (BGF) and biphase mark polarity + * correction (PC) bits are set to zero. + */ +uint32_t av_timecode_get_smpte_from_framenum(const AVTimecode *tc, int framenum); + +/** + * Load timecode string in buf. + * + * @param buf destination buffer, must be at least AV_TIMECODE_STR_SIZE long + * @param tc timecode data correctly initialized + * @param framenum frame number + * @return the buf parameter + * + * @note Timecode representation can be a negative timecode and have more than + * 24 hours, but will only be honored if the flags are correctly set. + * @note The frame number is relative to tc->start. + */ +char *av_timecode_make_string(const AVTimecode *tc, char *buf, int framenum); + +/** + * Get the timecode string from the SMPTE timecode format. + * + * @param buf destination buffer, must be at least AV_TIMECODE_STR_SIZE long + * @param tcsmpte the 32-bit SMPTE timecode + * @param prevent_df prevent the use of a drop flag when it is known the DF bit + * is arbitrary + * @return the buf parameter + */ +char *av_timecode_make_smpte_tc_string(char *buf, uint32_t tcsmpte, int prevent_df); + +/** + * Get the timecode string from the 25-bit timecode format (MPEG GOP format). + * + * @param buf destination buffer, must be at least AV_TIMECODE_STR_SIZE long + * @param tc25bit the 25-bits timecode + * @return the buf parameter + */ +char *av_timecode_make_mpeg_tc_string(char *buf, uint32_t tc25bit); + +/** + * Init a timecode struct with the passed parameters. + * + * @param log_ctx a pointer to an arbitrary struct of which the first field + * is a pointer to an AVClass struct (used for av_log) + * @param tc pointer to an allocated AVTimecode + * @param rate frame rate in rational form + * @param flags miscellaneous flags such as drop frame, +24 hours, ... + * (see AVTimecodeFlag) + * @param frame_start the first frame number + * @return 0 on success, AVERROR otherwise + */ +int av_timecode_init(AVTimecode *tc, AVRational rate, int flags, int frame_start, void *log_ctx); + +/** + * Parse timecode representation (hh:mm:ss[:;.]ff). + * + * @param log_ctx a pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct (used for av_log). + * @param tc pointer to an allocated AVTimecode + * @param rate frame rate in rational form + * @param str timecode string which will determine the frame start + * @return 0 on success, AVERROR otherwise + */ +int av_timecode_init_from_string(AVTimecode *tc, AVRational rate, const char *str, void *log_ctx); + +/** + * Check if the timecode feature is available for the given frame rate + * + * @return 0 if supported, <0 otherwise + */ +int av_timecode_check_frame_rate(AVRational rate); + +#endif /* AVUTIL_TIMECODE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timestamp.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timestamp.h new file mode 100644 index 0000000..e082f01 --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timestamp.h
@@ -0,0 +1,78 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * timestamp utils, mostly useful for debugging/logging purposes + */ + +#ifndef AVUTIL_TIMESTAMP_H +#define AVUTIL_TIMESTAMP_H + +#include "common.h" + +#if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64) +#error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS +#endif + +#define AV_TS_MAX_STRING_SIZE 32 + +/** + * Fill the provided buffer with a string containing a timestamp + * representation. + * + * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE + * @param ts the timestamp to represent + * @return the buffer in input + */ +static inline char *av_ts_make_string(char *buf, int64_t ts) +{ + if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS"); + else snprintf(buf, AV_TS_MAX_STRING_SIZE, "%" PRId64, ts); + return buf; +} + +/** + * Convenience macro, the return value should be used only directly in + * function arguments but never stand-alone. + */ +#define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts) + +/** + * Fill the provided buffer with a string containing a timestamp time + * representation. + * + * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE + * @param ts the timestamp to represent + * @param tb the timebase of the timestamp + * @return the buffer in input + */ +static inline char *av_ts_make_time_string(char *buf, int64_t ts, AVRational *tb) +{ + if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS"); + else snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts); + return buf; +} + +/** + * Convenience macro, the return value should be used only directly in + * function arguments but never stand-alone. + */ +#define av_ts2timestr(ts, tb) av_ts_make_time_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts, tb) + +#endif /* AVUTIL_TIMESTAMP_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tree.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tree.h new file mode 100644 index 0000000..d5e0aeb --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tree.h
@@ -0,0 +1,138 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * A tree container. + * @author Michael Niedermayer <michaelni@gmx.at> + */ + +#ifndef AVUTIL_TREE_H +#define AVUTIL_TREE_H + +#include "attributes.h" +#include "version.h" + +/** + * @addtogroup lavu_tree AVTree + * @ingroup lavu_data + * + * Low-complexity tree container + * + * Insertion, removal, finding equal, largest which is smaller than and + * smallest which is larger than, all have O(log n) worst-case complexity. + * @{ + */ + + +struct AVTreeNode; +extern const int av_tree_node_size; + +/** + * Allocate an AVTreeNode. + */ +struct AVTreeNode *av_tree_node_alloc(void); + +/** + * Find an element. + * @param root a pointer to the root node of the tree + * @param next If next is not NULL, then next[0] will contain the previous + * element and next[1] the next element. If either does not exist, + * then the corresponding entry in next is unchanged. + * @param cmp compare function used to compare elements in the tree, + * API identical to that of Standard C's qsort + * It is guaranteed that the first and only the first argument to cmp() + * will be the key parameter to av_tree_find(), thus it could if the + * user wants, be a different type (like an opaque context). + * @return An element with cmp(key, elem) == 0 or NULL if no such element + * exists in the tree. + */ +void *av_tree_find(const struct AVTreeNode *root, void *key, + int (*cmp)(const void *key, const void *b), void *next[2]); + +/** + * Insert or remove an element. + * + * If *next is NULL, then the supplied element will be removed if it exists. + * If *next is non-NULL, then the supplied element will be inserted, unless + * it already exists in the tree. + * + * @param rootp A pointer to a pointer to the root node of the tree; note that + * the root node can change during insertions, this is required + * to keep the tree balanced. + * @param key pointer to the element key to insert in the tree + * @param next Used to allocate and free AVTreeNodes. For insertion the user + * must set it to an allocated and zeroed object of at least + * av_tree_node_size bytes size. av_tree_insert() will set it to + * NULL if it has been consumed. + * For deleting elements *next is set to NULL by the user and + * av_tree_insert() will set it to the AVTreeNode which was + * used for the removed element. + * This allows the use of flat arrays, which have + * lower overhead compared to many malloced elements. + * You might want to define a function like: + * @code + * void *tree_insert(struct AVTreeNode **rootp, void *key, + * int (*cmp)(void *key, const void *b), + * AVTreeNode **next) + * { + * if (!*next) + * *next = av_mallocz(av_tree_node_size); + * return av_tree_insert(rootp, key, cmp, next); + * } + * void *tree_remove(struct AVTreeNode **rootp, void *key, + * int (*cmp)(void *key, const void *b, AVTreeNode **next)) + * { + * av_freep(next); + * return av_tree_insert(rootp, key, cmp, next); + * } + * @endcode + * @param cmp compare function used to compare elements in the tree, API identical + * to that of Standard C's qsort + * @return If no insertion happened, the found element; if an insertion or + * removal happened, then either key or NULL will be returned. + * Which one it is depends on the tree state and the implementation. You + * should make no assumptions that it's one or the other in the code. + */ +void *av_tree_insert(struct AVTreeNode **rootp, void *key, + int (*cmp)(const void *key, const void *b), + struct AVTreeNode **next); + +void av_tree_destroy(struct AVTreeNode *t); + +/** + * Apply enu(opaque, &elem) to all the elements in the tree in a given range. + * + * @param cmp a comparison function that returns < 0 for an element below the + * range, > 0 for an element above the range and == 0 for an + * element inside the range + * + * @note The cmp function should use the same ordering used to construct the + * tree. + */ +void av_tree_enumerate(struct AVTreeNode *t, void *opaque, + int (*cmp)(void *opaque, void *elem), + int (*enu)(void *opaque, void *elem)); + +/** + * @} + */ + +#endif /* AVUTIL_TREE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/twofish.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/twofish.h new file mode 100644 index 0000000..813cfec --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/twofish.h
@@ -0,0 +1,70 @@ +/* + * An implementation of the TwoFish algorithm + * Copyright (c) 2015 Supraja Meedinti + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_TWOFISH_H +#define AVUTIL_TWOFISH_H + +#include <stdint.h> + + +/** + * @file + * @brief Public header for libavutil TWOFISH algorithm + * @defgroup lavu_twofish TWOFISH + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_twofish_size; + +struct AVTWOFISH; + +/** + * Allocate an AVTWOFISH context + * To free the struct: av_free(ptr) + */ +struct AVTWOFISH *av_twofish_alloc(void); + +/** + * Initialize an AVTWOFISH context. + * + * @param ctx an AVTWOFISH context + * @param key a key of size ranging from 1 to 32 bytes used for encryption/decryption + * @param key_bits number of keybits: 128, 192, 256 If less than the required, padded with zeroes to nearest valid value; return value is 0 if key_bits is 128/192/256, -1 if less than 0, 1 otherwise + */ +int av_twofish_init(struct AVTWOFISH *ctx, const uint8_t *key, int key_bits); + +/** + * Encrypt or decrypt a buffer using a previously initialized context + * + * @param ctx an AVTWOFISH context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 16 byte blocks + * @paran iv initialization vector for CBC mode, NULL for ECB mode + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_twofish_crypt(struct AVTWOFISH *ctx, uint8_t *dst, const uint8_t *src, int count, uint8_t* iv, int decrypt); + +/** + * @} + */ +#endif /* AVUTIL_TWOFISH_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/version.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/version.h new file mode 100644 index 0000000..8f6da6a --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/version.h
@@ -0,0 +1,139 @@ +/* + * copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu + * Libavutil version macros + */ + +#ifndef AVUTIL_VERSION_H +#define AVUTIL_VERSION_H + +#include "macros.h" + +/** + * @addtogroup version_utils + * + * Useful to check and match library version in order to maintain + * backward compatibility. + * + * The FFmpeg libraries follow a versioning sheme very similar to + * Semantic Versioning (http://semver.org/) + * The difference is that the component called PATCH is called MICRO in FFmpeg + * and its value is reset to 100 instead of 0 to keep it above or equal to 100. + * Also we do not increase MICRO for every bugfix or change in git master. + * + * Prior to FFmpeg 3.2 point releases did not change any lib version number to + * avoid aliassing different git master checkouts. + * Starting with FFmpeg 3.2, the released library versions will occupy + * a separate MAJOR.MINOR that is not used on the master development branch. + * That is if we branch a release of master 55.10.123 we will bump to 55.11.100 + * for the release and master will continue at 55.12.100 after it. Each new + * point release will then bump the MICRO improving the usefulness of the lib + * versions. + * + * @{ + */ + +#define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c)) +#define AV_VERSION_DOT(a, b, c) a ##.## b ##.## c +#define AV_VERSION(a, b, c) AV_VERSION_DOT(a, b, c) + +/** + * Extract version components from the full ::AV_VERSION_INT int as returned + * by functions like ::avformat_version() and ::avcodec_version() + */ +#define AV_VERSION_MAJOR(a) ((a) >> 16) +#define AV_VERSION_MINOR(a) (((a) & 0x00FF00) >> 8) +#define AV_VERSION_MICRO(a) ((a) & 0xFF) + +/** + * @} + */ + +/** + * @defgroup lavu_ver Version and Build diagnostics + * + * Macros and function useful to check at compiletime and at runtime + * which version of libavutil is in use. + * + * @{ + */ + +#define LIBAVUTIL_VERSION_MAJOR 56 +#define LIBAVUTIL_VERSION_MINOR 22 +#define LIBAVUTIL_VERSION_MICRO 100 + +#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ + LIBAVUTIL_VERSION_MINOR, \ + LIBAVUTIL_VERSION_MICRO) +#define LIBAVUTIL_VERSION AV_VERSION(LIBAVUTIL_VERSION_MAJOR, \ + LIBAVUTIL_VERSION_MINOR, \ + LIBAVUTIL_VERSION_MICRO) +#define LIBAVUTIL_BUILD LIBAVUTIL_VERSION_INT + +#define LIBAVUTIL_IDENT "Lavu" AV_STRINGIFY(LIBAVUTIL_VERSION) + +/** + * @defgroup lavu_depr_guards Deprecation Guards + * FF_API_* defines may be placed below to indicate public API that will be + * dropped at a future version bump. The defines themselves are not part of + * the public API and may change, break or disappear at any time. + * + * @note, when bumping the major version it is recommended to manually + * disable each FF_API_* in its own commit instead of disabling them all + * at once through the bump. This improves the git bisect-ability of the change. + * + * @{ + */ + +#ifndef FF_API_VAAPI +#define FF_API_VAAPI (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_FRAME_QP +#define FF_API_FRAME_QP (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_PLUS1_MINUS1 +#define FF_API_PLUS1_MINUS1 (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_ERROR_FRAME +#define FF_API_ERROR_FRAME (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_PKT_PTS +#define FF_API_PKT_PTS (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_CRYPTO_SIZE_T +#define FF_API_CRYPTO_SIZE_T (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_FRAME_GET_SET +#define FF_API_FRAME_GET_SET (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_PSEUDOPAL +#define FF_API_PSEUDOPAL (LIBAVUTIL_VERSION_MAJOR < 57) +#endif + + +/** + * @} + * @} + */ + +#endif /* AVUTIL_VERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/xtea.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/xtea.h new file mode 100644 index 0000000..735427c --- /dev/null +++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/xtea.h
@@ -0,0 +1,94 @@ +/* + * A 32-bit implementation of the XTEA algorithm + * Copyright (c) 2012 Samuel Pitoiset + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_XTEA_H +#define AVUTIL_XTEA_H + +#include <stdint.h> + +/** + * @file + * @brief Public header for libavutil XTEA algorithm + * @defgroup lavu_xtea XTEA + * @ingroup lavu_crypto + * @{ + */ + +typedef struct AVXTEA { + uint32_t key[16]; +} AVXTEA; + +/** + * Allocate an AVXTEA context. + */ +AVXTEA *av_xtea_alloc(void); + +/** + * Initialize an AVXTEA context. + * + * @param ctx an AVXTEA context + * @param key a key of 16 bytes used for encryption/decryption, + * interpreted as big endian 32 bit numbers + */ +void av_xtea_init(struct AVXTEA *ctx, const uint8_t key[16]); + +/** + * Initialize an AVXTEA context. + * + * @param ctx an AVXTEA context + * @param key a key of 16 bytes used for encryption/decryption, + * interpreted as little endian 32 bit numbers + */ +void av_xtea_le_init(struct AVXTEA *ctx, const uint8_t key[16]); + +/** + * Encrypt or decrypt a buffer using a previously initialized context, + * in big endian format. + * + * @param ctx an AVXTEA context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, if NULL then ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_xtea_crypt(struct AVXTEA *ctx, uint8_t *dst, const uint8_t *src, + int count, uint8_t *iv, int decrypt); + +/** + * Encrypt or decrypt a buffer using a previously initialized context, + * in little endian format. + * + * @param ctx an AVXTEA context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, if NULL then ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_xtea_le_crypt(struct AVXTEA *ctx, uint8_t *dst, const uint8_t *src, + int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_XTEA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg_includes.gyp b/src/third_party/ffmpeg_includes/ffmpeg_includes.gyp index ef2977c..51e447c 100644 --- a/src/third_party/ffmpeg_includes/ffmpeg_includes.gyp +++ b/src/third_party/ffmpeg_includes/ffmpeg_includes.gyp
@@ -35,5 +35,12 @@ 'include_dirs': [ 'ffmpeg.57.107.100' ], }, }, + { + 'target_name': 'ffmpeg.58.35.100', + 'type': 'none', + 'direct_dependent_settings': { + 'include_dirs': [ 'ffmpeg.58.35.100' ], + }, + }, ], }
diff --git a/src/third_party/llvm-project/libunwind/libunwind.gyp b/src/third_party/llvm-project/libunwind/libunwind.gyp index 3a32bbd..0d2587c 100644 --- a/src/third_party/llvm-project/libunwind/libunwind.gyp +++ b/src/third_party/llvm-project/libunwind/libunwind.gyp
@@ -71,7 +71,6 @@ 'src/libunwind_ext.h', 'src/Registers.hpp', 'src/RWMutex.hpp', - 'src/Unwind_AppleExtras.cpp', 'src/UnwindCursor.hpp', 'src/Unwind-EHABI.cpp', 'src/Unwind-EHABI.h', @@ -91,6 +90,13 @@ 'LLVM_PATH="<(DEPTH)/third_party/llvm-project/llvm/"', ], }, + 'conditions': [ + ['target_os == "tvos"', { + 'sources': [ + 'src/Unwind_AppleExtras.cpp', + ], + }], + ], } ] }
diff --git a/src/third_party/mozjs-45/config/milestone.txt.orig b/src/third_party/mozjs-45/config/milestone.txt.orig deleted file mode 100644 index da9c934..0000000 --- a/src/third_party/mozjs-45/config/milestone.txt.orig +++ /dev/null
@@ -1 +0,0 @@ -44.0a2
diff --git a/src/third_party/mozjs-45/js/public/UbiNodeCensus.h.rej b/src/third_party/mozjs-45/js/public/UbiNodeCensus.h.rej deleted file mode 100644 index 070a25d..0000000 --- a/src/third_party/mozjs-45/js/public/UbiNodeCensus.h.rej +++ /dev/null
@@ -1,21 +0,0 @@ ---- UbiNodeCensus.h -+++ UbiNodeCensus.h -@@ -4,16 +4,18 @@ - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - #ifndef js_UbiNodeCensus_h - #define js_UbiNodeCensus_h - - #include "mozilla/Move.h" - -+#include "jsapi.h" -+ - #include "js/UbiNode.h" - #include "js/UbiNodeTraverse.h" - - // A census is a ubi::Node traversal that assigns each node to one or more - // buckets, and returns a report with the size of each bucket. - // - // We summarize the results of a census with counts broken down according to - // criteria selected by the API consumer code that is requesting the census. For
diff --git a/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/analyzeRoots.js.orig b/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/analyzeRoots.js.orig deleted file mode 100644 index 096c8c6..0000000 --- a/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/analyzeRoots.js.orig +++ /dev/null
@@ -1,802 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */ - -"use strict"; - -loadRelativeToScript('utility.js'); -loadRelativeToScript('annotations.js'); -loadRelativeToScript('CFG.js'); - -var sourceRoot = (os.getenv('SOURCE') || '') + '/' - -var functionName; -var functionBodies; - -if (typeof scriptArgs[0] != 'string' || typeof scriptArgs[1] != 'string') - throw "Usage: analyzeRoots.js [-f function_name] <gcFunctions.lst> <gcEdges.txt> <suppressedFunctions.lst> <gcTypes.txt> <typeInfo.txt> [start end [tmpfile]]"; - -var theFunctionNameToFind; -if (scriptArgs[0] == '--function') { - theFunctionNameToFind = scriptArgs[1]; - scriptArgs = scriptArgs.slice(2); -} - -var gcFunctionsFile = scriptArgs[0] || "gcFunctions.lst"; -var gcEdgesFile = scriptArgs[1] || "gcEdges.txt"; -var suppressedFunctionsFile = scriptArgs[2] || "suppressedFunctions.lst"; -var gcTypesFile = scriptArgs[3] || "gcTypes.txt"; -var typeInfoFile = scriptArgs[4] || "typeInfo.txt"; -var batch = (scriptArgs[5]|0) || 1; -var numBatches = (scriptArgs[6]|0) || 1; -var tmpfile = scriptArgs[7] || "tmp.txt"; - -GCSuppressionTypes.push(...loadTypeInfo(typeInfoFile)["Suppress GC"]); - -var gcFunctions = {}; -var text = snarf("gcFunctions.lst").split("\n"); -assert(text.pop().length == 0); -for (var line of text) - gcFunctions[mangled(line)] = true; - -var suppressedFunctions = {}; -var text = snarf(suppressedFunctionsFile).split("\n"); -assert(text.pop().length == 0); -for (var line of text) { - suppressedFunctions[line] = true; -} -text = null; - -var gcEdges = {}; -text = snarf(gcEdgesFile).split('\n'); -assert(text.pop().length == 0); -for (var line of text) { - var [ block, edge, func ] = line.split(" || "); - if (!(block in gcEdges)) - gcEdges[block] = {} - gcEdges[block][edge] = func; -} -text = null; - -for (var line of readFileLines_gen(typeInfoFile)) { - line = line.replace(/\n/, ""); - GCSuppressionTypes.push(line); -} - -var match; -var gcThings = {}; -var gcPointers = {}; - -text = snarf(gcTypesFile).split("\n"); -for (var line of text) { - if (match = /^GCThing: (.*)/.exec(line)) - gcThings[match[1]] = true; - if (match = /^GCPointer: (.*)/.exec(line)) - gcPointers[match[1]] = true; -} -text = null; - -function isGCType(type) -{ - if (type.Kind == "CSU") - return type.Name in gcThings; - else if (type.Kind == "Array") - return isGCType(type.Type); - return false; -} - -function isUnrootedType(type) -{ - if (type.Kind == "Pointer") - return isGCType(type.Type); - else if (type.Kind == "Array") - return isUnrootedType(type.Type); - else if (type.Kind == "CSU") - return type.Name in gcPointers; - else - return false; -} - -function expressionUsesVariable(exp, variable) -{ - if (exp.Kind == "Var" && sameVariable(exp.Variable, variable)) - return true; - if (!("Exp" in exp)) - return false; - for (var childExp of exp.Exp) { - if (expressionUsesVariable(childExp, variable)) - return true; - } - return false; -} - -function expressionUsesVariableContents(exp, variable) -{ - if (!("Exp" in exp)) - return false; - for (var childExp of exp.Exp) { - if (childExp.Kind == 'Drf') { - if (expressionUsesVariable(childExp, variable)) - return true; - } else if (expressionUsesVariableContents(childExp, variable)) { - return true; - } - } - return false; -} - -// Detect simple |return nullptr;| statements. -function isReturningImmobileValue(edge, variable) -{ - if (variable.Kind == "Return") { - if (edge.Exp[0].Kind == "Var" && sameVariable(edge.Exp[0].Variable, variable)) { - if (edge.Exp[1].Kind == "Int" && edge.Exp[1].String == "0") { - return true; - } - } - } - return false; -} - -// If the edge uses the given variable, return the earliest point at which the -// use is definite. Usually, that means the source of the edge (anything that -// reaches that source point will end up using the variable, but there may be -// other ways to reach the destination of the edge.) -// -// Return values are implicitly used at the very last point in the function. -// This makes a difference: if an RAII class GCs in its destructor, we need to -// start looking at the final point in the function, not one point back from -// that, since that would skip over the GCing call. -// -function edgeUsesVariable(edge, variable, body) -{ - if (ignoreEdgeUse(edge, variable, body)) - return 0; - - if (variable.Kind == "Return" && body.Index[1] == edge.Index[1] && body.BlockId.Kind == "Function") - return edge.Index[1]; // Last point in function body uses the return value. - - var src = edge.Index[0]; - - switch (edge.Kind) { - - case "Assign": - if (isReturningImmobileValue(edge, variable)) - return 0; - if (expressionUsesVariable(edge.Exp[0], variable)) - return src; - return expressionUsesVariable(edge.Exp[1], variable) ? src : 0; - - case "Assume": - return expressionUsesVariableContents(edge.Exp[0], variable) ? src : 0; - - case "Call": - if (expressionUsesVariable(edge.Exp[0], variable)) - return src; - if (1 in edge.Exp && expressionUsesVariable(edge.Exp[1], variable)) - return src; - if ("PEdgeCallInstance" in edge) { - if (expressionUsesVariable(edge.PEdgeCallInstance.Exp, variable)) - return src; - } - if ("PEdgeCallArguments" in edge) { - for (var exp of edge.PEdgeCallArguments.Exp) { - if (expressionUsesVariable(exp, variable)) - return src; - } - } - return 0; - - case "Loop": - return 0; - - default: - assert(false); - } -} - -function expressionIsVariableAddress(exp, variable) -{ - while (exp.Kind == "Fld") - exp = exp.Exp[0]; - return exp.Kind == "Var" && sameVariable(exp.Variable, variable); -} - -function edgeTakesVariableAddress(edge, variable, body) -{ - if (ignoreEdgeUse(edge, variable, body)) - return false; - if (ignoreEdgeAddressTaken(edge)) - return false; - switch (edge.Kind) { - case "Assign": - return expressionIsVariableAddress(edge.Exp[1], variable); - case "Call": - if ("PEdgeCallArguments" in edge) { - for (var exp of edge.PEdgeCallArguments.Exp) { - if (expressionIsVariableAddress(exp, variable)) - return true; - } - } - return false; - default: - return false; - } -} - -function edgeKillsVariable(edge, variable) -{ - // Direct assignments kill their lhs: var = value - if (edge.Kind == "Assign") { - var lhs = edge.Exp[0]; - if (lhs.Kind == "Var" && sameVariable(lhs.Variable, variable)) - return !isReturningImmobileValue(edge, variable); - } - - if (edge.Kind != "Call") - return false; - - // Assignments of call results kill their lhs. - if (1 in edge.Exp) { - var lhs = edge.Exp[1]; - if (lhs.Kind == "Var" && sameVariable(lhs.Variable, variable)) - return true; - } - - // Constructor calls kill their 'this' value. - if ("PEdgeCallInstance" in edge) { - do { - var instance = edge.PEdgeCallInstance.Exp; - - // Kludge around incorrect dereference on some constructor calls. - if (instance.Kind == "Drf") - instance = instance.Exp[0]; - - if (instance.Kind != "Var" || !sameVariable(instance.Variable, variable)) - break; - - var callee = edge.Exp[0]; - if (callee.Kind != "Var") - break; - - assert(callee.Variable.Kind == "Func"); - var calleeName = readable(callee.Variable.Name[0]); - - // Constructor calls include the text 'Name::Name(' or 'Name<...>::Name('. - var openParen = calleeName.indexOf('('); - if (openParen < 0) - break; - calleeName = calleeName.substring(0, openParen); - - var lastColon = calleeName.lastIndexOf('::'); - if (lastColon < 0) - break; - var constructorName = calleeName.substr(lastColon + 2); - calleeName = calleeName.substr(0, lastColon); - - var lastTemplateOpen = calleeName.lastIndexOf('<'); - if (lastTemplateOpen >= 0) - calleeName = calleeName.substr(0, lastTemplateOpen); - - if (calleeName.endsWith(constructorName)) - return true; - } while (false); - } - - return false; -} - -function edgeCanGC(edge) -{ - if (edge.Kind != "Call") - return false; - - var callee = edge.Exp[0]; - - while (callee.Kind == "Drf") - callee = callee.Exp[0]; - - if (callee.Kind == "Var") { - var variable = callee.Variable; - - if (variable.Kind == "Func") { - var callee = mangled(variable.Name[0]); - if ((callee in gcFunctions) || ((callee + internalMarker) in gcFunctions)) - return "'" + variable.Name[0] + "'"; - return null; - } - - var varName = variable.Name[0]; - return indirectCallCannotGC(functionName, varName) ? null : "*" + varName; - } - - if (callee.Kind == "Fld") { - var field = callee.Field; - var csuName = field.FieldCSU.Type.Name; - var fullFieldName = csuName + "." + field.Name[0]; - if (fieldCallCannotGC(csuName, fullFieldName)) - return null; - return (fullFieldName in suppressedFunctions) ? null : fullFieldName; - } -} - -// Search recursively through predecessors from a variable use, returning -// whether a GC call is reachable (in the reverse direction; this means that -// the variable use is reachable from the GC call, and therefore the variable -// is live after the GC call), along with some additional information. What -// info we want depends on whether the variable turns out to be live across any -// GC call. We are looking for both hazards (unrooted variables live across GC -// calls) and unnecessary roots (rooted variables that have no GC calls in -// their live ranges.) -// -// If not: -// -// - 'minimumUse': the earliest point in each body that uses the variable, for -// reporting on unnecessary roots. -// -// If so: -// -// - 'why': a path from the GC call to a use of the variable after the GC -// call, chained through a 'why' field in the returned edge descriptor -// -// - 'gcInfo': a direct pointer to the GC call edge -// -function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) -{ - // Scan through all edges preceding an unrooted variable use, using an - // explicit worklist, looking for a GC call. A worklist contains an - // incoming edge together with a description of where it or one of its - // successors GC'd (if any). - - var bodies_visited = new Map(); - - let worklist = [{body: start_body, ppoint: start_point, preGCLive: false, gcInfo: null, why: null}]; - while (worklist.length) { - // Grab an entry off of the worklist, representing a point within the - // CFG identified by <body,ppoint>. If this point has a descendant - // later in the CFG that can GC, gcInfo will be set to the information - // about that GC call. - - var entry = worklist.pop(); - var { body, ppoint, gcInfo, preGCLive } = entry; - - // Handle the case where there are multiple ways to reach this point - // (traversing backwards). - var visited = bodies_visited.get(body); - if (!visited) - bodies_visited.set(body, visited = new Map()); - if (visited.has(ppoint)) { - var seenEntry = visited.get(ppoint); - - // This point already knows how to GC through some other path, so - // we have nothing new to learn. (The other path will consider the - // predecessors.) - if (seenEntry.gcInfo) - continue; - - // If this worklist's entry doesn't know of any way to GC, then - // there's no point in continuing the traversal through it. Perhaps - // another edge will be found that *can* GC; otherwise, the first - // route to the point will traverse through predecessors. - // - // Note that this means we may visit a point more than once, if the - // first time we visit we don't have a known reachable GC call and - // the second time we do. - if (!gcInfo) - continue; - } - visited.set(ppoint, {body: body, gcInfo: gcInfo}); - - // Check for hitting the entry point of the current body (which may be - // the outer function or a loop within it.) - if (ppoint == body.Index[0]) { - if (body.BlockId.Kind == "Loop") { - // Propagate to outer body parents that enter the loop body. - if ("BlockPPoint" in body) { - for (var parent of body.BlockPPoint) { - var found = false; - for (var xbody of functionBodies) { - if (sameBlockId(xbody.BlockId, parent.BlockId)) { - assert(!found); - found = true; - worklist.push({body: xbody, ppoint: parent.Index, - gcInfo: gcInfo, why: entry}); - } - } - assert(found); - } - } - } else if (variable.Kind == "Arg" && gcInfo) { - // The scope of arguments starts at the beginning of the - // function - return entry; - } else if (entry.preGCLive) { - // We didn't find a "good" explanation beginning of the live - // range, but we do know the variable was live across the GC. - // This can happen if the live range started when a variable is - // used as a retparam. - return entry; - } - } - - var predecessors = getPredecessors(body); - if (!(ppoint in predecessors)) - continue; - - for (var edge of predecessors[ppoint]) { - var source = edge.Index[0]; - - var edge_kills = edgeKillsVariable(edge, variable); - var edge_uses = edgeUsesVariable(edge, variable, body); - - if (edge_kills || edge_uses) { - if (!body.minimumUse || source < body.minimumUse) - body.minimumUse = source; - } - - if (edge_kills) { - // This is a beginning of the variable's live range. If we can - // reach a GC call from here, then we're done -- we have a path - // from the beginning of the live range, through the GC call, - // to a use after the GC call that proves its live range - // extends at least that far. - if (gcInfo) - return {body: body, ppoint: source, gcInfo: gcInfo, why: entry }; - - // Otherwise, keep searching through the graph, but truncate - // this particular branch of the search at this edge. - continue; - } - - var src_gcInfo = gcInfo; - var src_preGCLive = preGCLive; - if (!gcInfo && !(source in body.suppressed) && !suppressed) { - var gcName = edgeCanGC(edge, body); - if (gcName) - src_gcInfo = {name:gcName, body:body, ppoint:source}; - } - - if (edge_uses) { - // The live range starts at least this far back, so we're done - // for the same reason as with edge_kills. The only difference - // is that a GC on this edge indicates a hazard, whereas if - // we're killing a live range in the GC call then it's not live - // *across* the call. - // - // However, we may want to generate a longer usage chain for - // the variable than is minimally necessary. For example, - // consider: - // - // Value v = f(); - // if (v.isUndefined()) - // return false; - // gc(); - // return v; - // - // The call to .isUndefined() is considered to be a use and - // therefore indicates that v must be live at that point. But - // it's more helpful to the user to continue the 'why' path to - // include the ancestor where the value was generated. So we - // will only return here if edge.Kind is Assign; otherwise, - // we'll pass a "preGCLive" value up through the worklist to - // remember that the variable *is* alive before the GC and so - // this function should be returning a true value. - - if (src_gcInfo) { - src_preGCLive = true; - if (edge.Kind == 'Assign') - return {body:body, ppoint:source, gcInfo:src_gcInfo, why:entry}; - } - } - - if (edge.Kind == "Loop") { - // Additionally propagate the search into a loop body, starting - // with the exit point. - var found = false; - for (var xbody of functionBodies) { - if (sameBlockId(xbody.BlockId, edge.BlockId)) { - assert(!found); - found = true; - worklist.push({body:xbody, ppoint:xbody.Index[1], - preGCLive: src_preGCLive, gcInfo:src_gcInfo, - why:entry}); - } - } - assert(found); - break; - } - - // Propagate the search to the predecessors of this edge. - worklist.push({body:body, ppoint:source, - preGCLive: src_preGCLive, gcInfo:src_gcInfo, - why:entry}); - } - } - - return null; -} - -function variableLiveAcrossGC(suppressed, variable) -{ - // A variable is live across a GC if (1) it is used by an edge (as in, it - // was at least initialized), and (2) it is used after a GC in a successor - // edge. - - for (var body of functionBodies) - body.minimumUse = 0; - - for (var body of functionBodies) { - if (!("PEdge" in body)) - continue; - for (var edge of body.PEdge) { - var usePoint = edgeUsesVariable(edge, variable, body); - // Example for !edgeKillsVariable: - // - // JSObject* obj = NewObject(); - // cangc(); - // obj = NewObject(); <-- uses 'obj', but kills previous value - // - if (usePoint && !edgeKillsVariable(edge, variable)) { - // Found a use, possibly after a GC. - var call = findGCBeforeVariableUse(body, usePoint, suppressed, variable); - if (!call) - continue; - - call.afterGCUse = usePoint; - return call; - } - } - } - return null; -} - -// An unrooted variable has its address stored in another variable via -// assignment, or passed into a function that can GC. If the address is -// assigned into some other variable, we can't track it to see if it is held -// live across a GC. If it is passed into a function that can GC, then it's -// sort of like a Handle to an unrooted location, and the callee could GC -// before overwriting it or rooting it. -function unsafeVariableAddressTaken(suppressed, variable) -{ - for (var body of functionBodies) { - if (!("PEdge" in body)) - continue; - for (var edge of body.PEdge) { - if (edgeTakesVariableAddress(edge, variable, body)) { - if (edge.Kind == "Assign" || (!suppressed && edgeCanGC(edge))) - return {body:body, ppoint:edge.Index[0]}; - } - } - } - return null; -} - -// Read out the brief (non-JSON, semi-human-readable) CFG description for the -// given function and store it. -function loadPrintedLines(functionName) -{ - assert(!os.system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile)); - var lines = snarf(tmpfile).split('\n'); - - for (var body of functionBodies) - body.lines = []; - - // Distribute lines of output to the block they originate from. - var currentBody = null; - for (var line of lines) { - if (/^block:/.test(line)) { - if (match = /:(loop#[\d#]+)/.exec(line)) { - var loop = match[1]; - var found = false; - for (var body of functionBodies) { - if (body.BlockId.Kind == "Loop" && body.BlockId.Loop == loop) { - assert(!found); - found = true; - currentBody = body; - } - } - assert(found); - } else { - for (var body of functionBodies) { - if (body.BlockId.Kind == "Function") - currentBody = body; - } - } - } - if (currentBody) - currentBody.lines.push(line); - } -} - -function findLocation(body, ppoint, opts={brief: false}) -{ - var location = body.PPoint[ppoint - 1].Location; - var file = location.CacheString; - - if (file.indexOf(sourceRoot) == 0) - file = file.substring(sourceRoot.length); - - if (opts.brief) { - var m = /.*\/(.*)/.exec(file); - if (m) - file = m[1]; - } - - return file + ":" + location.Line; -} - -function locationLine(text) -{ - if (match = /:(\d+)$/.exec(text)) - return match[1]; - return 0; -} - -function printEntryTrace(functionName, entry) -{ - var gcPoint = entry.gcInfo ? entry.gcInfo.ppoint : 0; - - if (!functionBodies[0].lines) - loadPrintedLines(functionName); - - while (entry) { - var ppoint = entry.ppoint; - var lineText = findLocation(entry.body, ppoint, {"brief": true}); - - var edgeText = ""; - if (entry.why && entry.why.body == entry.body) { - // If the next point in the trace is in the same block, look for an edge between them. - var next = entry.why.ppoint; - - if (!entry.body.edgeTable) { - var table = {}; - entry.body.edgeTable = table; - for (var line of entry.body.lines) { - if (match = /\((\d+,\d+),/.exec(line)) - table[match[1]] = line; // May be multiple? - } - } - - edgeText = entry.body.edgeTable[ppoint + "," + next]; - assert(edgeText); - if (ppoint == gcPoint) - edgeText += " [[GC call]]"; - } else { - // Look for any outgoing edge from the chosen point. - for (var line of entry.body.lines) { - if (match = /\((\d+),/.exec(line)) { - if (match[1] == ppoint) { - edgeText = line; - break; - } - } - } - if (ppoint == entry.body.Index[1] && entry.body.BlockId.Kind == "Function") - edgeText += " [[end of function]]"; - } - - print(" " + lineText + (edgeText.length ? ": " + edgeText : "")); - entry = entry.why; - } -} - -function isRootedType(type) -{ - return type.Kind == "CSU" && isRootedTypeName(type.Name); -} - -function typeDesc(type) -{ - if (type.Kind == "CSU") { - return type.Name; - } else if ('Type' in type) { - var inner = typeDesc(type.Type); - if (type.Kind == 'Pointer') - return inner + '*'; - else if (type.Kind == 'Array') - return inner + '[]'; - else - return inner + '?'; - } else { - return '???'; - } -} - -function processBodies(functionName) -{ - if (!("DefineVariable" in functionBodies[0])) - return; - var suppressed = (mangled(functionName) in suppressedFunctions); - for (var variable of functionBodies[0].DefineVariable) { - var name; - if (variable.Variable.Kind == "This") - name = "this"; - else if (variable.Variable.Kind == "Return") - name = "<returnvalue>"; - else - name = variable.Variable.Name[0]; - - if (isRootedType(variable.Type)) { - if (!variableLiveAcrossGC(suppressed, variable.Variable)) { - // The earliest use of the variable should be its constructor. - var lineText; - for (var body of functionBodies) { - if (body.minimumUse) { - var text = findLocation(body, body.minimumUse); - if (!lineText || locationLine(lineText) > locationLine(text)) - lineText = text; - } - } - print("\nFunction '" + functionName + "'" + - " has unnecessary root '" + name + "' at " + lineText); - } - } else if (isUnrootedType(variable.Type)) { - var result = variableLiveAcrossGC(suppressed, variable.Variable); - if (result) { - var lineText = findLocation(result.gcInfo.body, result.gcInfo.ppoint); - print("\nFunction '" + functionName + "'" + - " has unrooted '" + name + "'" + - " of type '" + typeDesc(variable.Type) + "'" + - " live across GC call " + result.gcInfo.name + - " at " + lineText); - printEntryTrace(functionName, result); - } - result = unsafeVariableAddressTaken(suppressed, variable.Variable); - if (result) { - var lineText = findLocation(result.body, result.ppoint); - print("\nFunction '" + functionName + "'" + - " takes unsafe address of unrooted '" + name + "'" + - " at " + lineText); - printEntryTrace(functionName, {body:result.body, ppoint:result.ppoint}); - } - } - } -} - -if (batch == 1) - print("Time: " + new Date); - -var xdb = xdbLibrary(); -xdb.open("src_body.xdb"); - -var minStream = xdb.min_data_stream()|0; -var maxStream = xdb.max_data_stream()|0; - -var N = (maxStream - minStream) + 1; -var start = Math.floor((batch - 1) / numBatches * N) + minStream; -var start_next = Math.floor(batch / numBatches * N) + minStream; -var end = start_next - 1; - -function process(name, json) { - functionName = name; - functionBodies = JSON.parse(json); - - for (var body of functionBodies) - body.suppressed = []; - for (var body of functionBodies) { - for (var [pbody, id] of allRAIIGuardedCallPoints(functionBodies, body, isSuppressConstructor)) - pbody.suppressed[id] = true; - } - processBodies(functionName); -} - -if (theFunctionNameToFind) { - var data = xdb.read_entry(theFunctionNameToFind); - var json = data.readString(); - process(theFunctionNameToFind, json); - xdb.free_string(data); - quit(0); -} - -for (var nameIndex = start; nameIndex <= end; nameIndex++) { - var name = xdb.read_key(nameIndex); - var functionName = name.readString(); - var data = xdb.read_entry(name); - xdb.free_string(name); - var json = data.readString(); - try { - process(functionName, json); - } catch (e) { - printErr("Exception caught while handling " + functionName); - throw(e); - } - xdb.free_string(data); -}
diff --git a/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/annotations.js.orig b/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/annotations.js.orig deleted file mode 100644 index 671f797..0000000 --- a/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/annotations.js.orig +++ /dev/null
@@ -1,379 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */ - -"use strict"; - -// RAII types within which we should assume GC is suppressed, eg -// AutoSuppressGC. -var GCSuppressionTypes = []; - -// Ignore calls made through these function pointers -var ignoreIndirectCalls = { - "mallocSizeOf" : true, - "aMallocSizeOf" : true, - "_malloc_message" : true, - "je_malloc_message" : true, - "chunk_dalloc" : true, - "chunk_alloc" : true, - "__conv" : true, - "__convf" : true, - "prerrortable.c:callback_newtable" : true, - "mozalloc_oom.cpp:void (* gAbortHandler)(size_t)" : true, -}; - -function indirectCallCannotGC(fullCaller, fullVariable) -{ - var caller = readable(fullCaller); - - // This is usually a simple variable name, but sometimes a full name gets - // passed through. And sometimes that name is truncated. Examples: - // _ZL13gAbortHandler|mozalloc_oom.cpp:void (* gAbortHandler)(size_t) - // _ZL14pMutexUnlockFn|umutex.cpp:void (* pMutexUnlockFn)(const void* - var name = readable(fullVariable); - - if (name in ignoreIndirectCalls) - return true; - - if (name == "mapper" && caller == "ptio.c:pt_MapError") - return true; - - if (name == "params" && caller == "PR_ExplodeTime") - return true; - - if (name == "op" && /GetWeakmapKeyDelegate/.test(caller)) - return true; - - // hook called during script finalization which cannot GC. - if (/CallDestroyScriptHook/.test(caller)) - return true; - - // template method called during marking and hence cannot GC - if (name == "op" && caller.indexOf("bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark(JSObject*)") != -1) - { - return true; - } - - return false; -} - -// Ignore calls through functions pointers with these types -var ignoreClasses = { - "JSStringFinalizer" : true, - "SprintfState" : true, - "SprintfStateStr" : true, - "JSLocaleCallbacks" : true, - "JSC::ExecutableAllocator" : true, - "PRIOMethods": true, - "XPCOMFunctions" : true, // I'm a little unsure of this one - "_MD_IOVector" : true, - "malloc_table_t": true, // replace_malloc - "malloc_hook_table_t": true, // replace_malloc -}; - -// Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing -// a function pointer field named FIELD. -var ignoreCallees = { - "js::Class.trace" : true, - "js::Class.finalize" : true, - "JSRuntime.destroyPrincipals" : true, - "icu_50::UObject.__deleting_dtor" : true, // destructors in ICU code can't cause GC - "mozilla::CycleCollectedJSRuntime.DescribeCustomObjects" : true, // During tracing, cannot GC. - "mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC. - "PLDHashTableOps.hashKey" : true, - "z_stream_s.zfree" : true, - "z_stream_s.zalloc" : true, - "GrGLInterface.fCallback" : true, - "std::strstreambuf._M_alloc_fun" : true, - "std::strstreambuf._M_free_fun" : true, - "struct js::gc::Callback<void (*)(JSRuntime*, void*)>.op" : true, -}; - -function fieldCallCannotGC(csu, fullfield) -{ - if (csu in ignoreClasses) - return true; - if (fullfield in ignoreCallees) - return true; - return false; -} - -function ignoreEdgeUse(edge, variable, body) -{ - // Horrible special case for ignoring a false positive in xptcstubs: there - // is a local variable 'paramBuffer' holding an array of nsXPTCMiniVariant - // on the stack, which appears to be live across a GC call because its - // constructor is called when the array is initialized, even though the - // constructor is a no-op. So we'll do a very narrow exclusion for the use - // that incorrectly started the live range, which was basically "__temp_1 = - // paramBuffer". - // - // By scoping it so narrowly, we can detect most hazards that would be - // caused by modifications in the PrepareAndDispatch code. It just barely - // avoids having a hazard already. - if (('Name' in variable) && (variable.Name[0] == 'paramBuffer')) { - if (body.BlockId.Kind == 'Function' && body.BlockId.Variable.Name[0] == 'PrepareAndDispatch') - if (edge.Kind == 'Assign' && edge.Type.Kind == 'Pointer') - if (edge.Exp[0].Kind == 'Var' && edge.Exp[1].Kind == 'Var') - if (edge.Exp[1].Variable.Kind == 'Local' && edge.Exp[1].Variable.Name[0] == 'paramBuffer') - return true; - } - - // Functions which should not be treated as using variable. - if (edge.Kind == "Call") { - var callee = edge.Exp[0]; - if (callee.Kind == "Var") { - var name = callee.Variable.Name[0]; - if (/~DebugOnly/.test(name)) - return true; - if (/~ScopedThreadSafeStringInspector/.test(name)) - return true; - } - } - - return false; -} - -function ignoreEdgeAddressTaken(edge) -{ - // Functions which may take indirect pointers to unrooted GC things, - // but will copy them into rooted locations before calling anything - // that can GC. These parameters should usually be replaced with - // handles or mutable handles. - if (edge.Kind == "Call") { - var callee = edge.Exp[0]; - if (callee.Kind == "Var") { - var name = callee.Variable.Name[0]; - if (/js::Invoke\(/.test(name)) - return true; - } - } - - return false; -} - -// Return whether csu.method is one that we claim can never GC. -function isSuppressedVirtualMethod(csu, method) -{ - return csu == "nsISupports" && (method == "AddRef" || method == "Release"); -} - -// Ignore calls of these functions (so ignore any stack containing these) -var ignoreFunctions = { - "ptio.c:pt_MapError" : true, - "je_malloc_printf" : true, - "PR_ExplodeTime" : true, - "PR_ErrorInstallTable" : true, - "PR_SetThreadPrivate" : true, - "JSObject* js::GetWeakmapKeyDelegate(JSObject*)" : true, // FIXME: mark with AutoSuppressGCAnalysis instead - "uint8 NS_IsMainThread()" : true, - - // Has an indirect call under it by the name "__f", which seemed too - // generic to ignore by itself. - "void* std::_Locale_impl::~_Locale_impl(int32)" : true, - - // Bug 1056410 - devirtualization prevents the standard nsISupports::Release heuristic from working - "uint32 nsXPConnect::Release()" : true, - - // FIXME! - "NS_LogInit": true, - "NS_LogTerm": true, - "NS_LogAddRef": true, - "NS_LogRelease": true, - "NS_LogCtor": true, - "NS_LogDtor": true, - "NS_LogCOMPtrAddRef": true, - "NS_LogCOMPtrRelease": true, - - // FIXME! - "NS_DebugBreak": true, - - // These are a little overzealous -- these destructors *can* GC if they end - // up wrapping a pending exception. See bug 898815 for the heavyweight fix. - "void js::AutoCompartment::~AutoCompartment(int32)" : true, - "void JSAutoCompartment::~JSAutoCompartment(int32)" : true, - - // Bug 948646 - the only thing AutoJSContext's constructor calls - // is an Init() routine whose entire body is covered with an - // AutoSuppressGCAnalysis. AutoSafeJSContext is the same thing, just with - // a different value for the 'aSafe' parameter. - "void mozilla::AutoJSContext::AutoJSContext(mozilla::detail::GuardObjectNotifier*)" : true, - "void mozilla::AutoSafeJSContext::~AutoSafeJSContext(int32)" : true, - - // And these are workarounds to avoid even more analysis work, - // which would sadly still be needed even with bug 898815. - "void js::AutoCompartment::AutoCompartment(js::ExclusiveContext*, JSCompartment*)": true, - - // The nsScriptNameSpaceManager functions can't actually GC. They - // just use a PLDHashTable which has function pointers, which makes the - // analysis think maybe they can. - "nsGlobalNameStruct* nsScriptNameSpaceManager::LookupNavigatorName(nsAString_internal*)": true, - "nsGlobalNameStruct* nsScriptNameSpaceManager::LookupName(nsAString_internal*, uint16**)": true, - - // Similar to heap snapshot mock classes, and GTests below. This posts a - // synchronous runnable when a GTest fails, and we are pretty sure that the - // particular runnable it posts can't even GC, but the analysis isn't - // currently smart enough to determine that. In either case, this is (a) - // only in GTests, and (b) only when the Gtest has already failed. We have - // static and dynamic checks for no GC in the non-test code, and in the test - // code we fall back to only the dynamic checks. - "void test::RingbufferDumper::OnTestPartResult(testing::TestPartResult*)" : true, - - "float64 JS_GetCurrentEmbedderTime()" : true, - - "uint64 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true, - "uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true, -}; - -function isProtobuf(name) -{ - return name.match(/\bgoogle::protobuf\b/) || - name.match(/\bmozilla::devtools::protobuf\b/); -} - -function isHeapSnapshotMockClass(name) -{ - return name.match(/\bMockWriter\b/) || - name.match(/\bMockDeserializedNode\b/); -} - -function isGTest(name) -{ - return name.match(/\btesting::/); -} - -function ignoreGCFunction(mangled) -{ - assert(mangled in readableNames); - var fun = readableNames[mangled][0]; - - if (fun in ignoreFunctions) - return true; - - // The protobuf library, and [de]serialization code generated by the - // protobuf compiler, uses a _ton_ of function pointers but they are all - // internal. Easiest to just ignore that mess here. - if (isProtobuf(fun)) - return true; - - // Ignore anything that goes through heap snapshot GTests or mocked classes - // used in heap snapshot GTests. GTest and GMock expose a lot of virtual - // methods and function pointers that could potentially GC after an - // assertion has already failed (depending on user-provided code), but don't - // exhibit that behavior currently. For non-test code, we have dynamic and - // static checks that ensure we don't GC. However, for test code we opt out - // of static checks here, because of the above stated GMock/GTest issues, - // and rely on only the dynamic checks provided by AutoAssertCannotGC. - if (isHeapSnapshotMockClass(fun) || isGTest(fun)) - return true; - - // Templatized function - if (fun.indexOf("void nsCOMPtr<T>::Assert_NoQueryNeeded()") >= 0) - return true; - - // These call through an 'op' function pointer. - if (fun.indexOf("js::WeakMap<Key, Value, HashPolicy>::getDelegate(") >= 0) - return true; - - // XXX modify refillFreeList<NoGC> to not need data flow analysis to understand it cannot GC. - if (/refillFreeList/.test(fun) && /\(js::AllowGC\)0u/.test(fun)) - return true; - return false; -} - -function stripUCSAndNamespace(name) -{ - name = name.replace(/(struct|class|union|const) /g, ""); - name = name.replace(/(js::ctypes::|js::|JS::|mozilla::dom::|mozilla::)/g, ""); - return name; -} - -function isRootedGCTypeName(name) -{ - return (name == "JSAddonId"); -} - -function isRootedGCPointerTypeName(name) -{ - name = stripUCSAndNamespace(name); - - if (name.startsWith('MaybeRooted<')) - return /\(js::AllowGC\)1u>::RootType/.test(name); - - if (name == "ErrorResult" || - name == "JSErrorResult" || - name == "WrappableJSErrorResult" || - name == "frontend::TokenStream" || - name == "frontend::TokenStream::Position" || - name == "ModuleValidator") - { - return true; - } - - return name.startsWith('Rooted') || name.startsWith('PersistentRooted'); -} - -function isRootedTypeName(name) -{ - return isRootedGCTypeName(name) || isRootedGCPointerTypeName(name); -} - -function isUnsafeStorage(typeName) -{ - typeName = stripUCSAndNamespace(typeName); - return typeName.startsWith('UniquePtr<'); -} - -function isSuppressConstructor(funcName) -{ - let [ qualifiedFunction, unqualifiedFunction ] = funcName; - let [ mangled, unmangled ] = splitFunction(qualifiedFunction); - if (!mangled.match(/C\dE/)) - return false; // Constructors have C1E (or C4E etc.) in their mangled names - let m = unmangled.match(/((\w+)(<.*>)?)::\2\(/); // Foo<T>::Foo - if (!m) - return false; - let classType = m[1]; // Foo<T> - for (let type of GCSuppressionTypes) { - // type is something like class js::Foo<T> - if (type.endsWith(classType)) { - // Screen out js::OtherFoo - if (type == classType || type.endsWith("::" + classType) || type.endsWith(" " + classType)) - return true; - } - } - return false; -} - -// nsISupports subclasses' methods may be scriptable (or overridden -// via binary XPCOM), and so may GC. But some fields just aren't going -// to get overridden with something that can GC. -function isOverridableField(initialCSU, csu, field) -{ - if (csu != 'nsISupports') - return false; - if (field == 'GetCurrentJSContext') - return false; - if (field == 'IsOnCurrentThread') - return false; - if (field == 'GetNativeContext') - return false; - if (field == "GetGlobalJSObject") - return false; - if (field == "GetIsMainThread") - return false; - if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject') - return false; - if (initialCSU == 'nsIXPConnect' && field == 'GetSafeJSContext') - return false; - if (initialCSU == 'nsIScriptContext') { - if (field == 'GetWindowProxy' || field == 'GetWindowProxyPreserveColor') - return false; - } - return true; -} - -function listNonGCPointers() { - return [ - // Safe only because jsids are currently only made from pinned strings. - 'NPIdentifier', - ]; -}
diff --git a/src/third_party/mozjs-45/js/src/frontend/BytecodeCompiler.cpp.rej b/src/third_party/mozjs-45/js/src/frontend/BytecodeCompiler.cpp.rej deleted file mode 100644 index b4299cb..0000000 --- a/src/third_party/mozjs-45/js/src/frontend/BytecodeCompiler.cpp.rej +++ /dev/null
@@ -1,109 +0,0 @@ ---- BytecodeCompiler.cpp -+++ BytecodeCompiler.cpp -@@ -22,16 +22,36 @@ - - #include "frontend/Parser-inl.h" - #include "vm/ScopeObject-inl.h" - - using namespace js; - using namespace js::frontend; - using mozilla::Maybe; - -+class MOZ_STACK_CLASS AutoCompilationTraceLogger -+{ -+ public: -+ AutoCompilationTraceLogger(ExclusiveContext* cx, const TraceLoggerTextId id); -+ -+ private: -+ TraceLoggerThread* logger; -+ TraceLoggerEvent event; -+ AutoTraceLog scriptLogger; -+ AutoTraceLog typeLogger; -+}; -+ -+AutoCompilationTraceLogger::AutoCompilationTraceLogger(ExclusiveContext* cx, const TraceLoggerTextId id) -+ : logger(cx->isJSContext() ? TraceLoggerForMainThread(cx->asJSContext()->runtime()) -+ : TraceLoggerForCurrentThread()), -+ event(logger, TraceLogger_AnnotateScripts), -+ scriptLogger(logger, event), -+ typeLogger(logger, id) -+{} -+ - static bool - CheckLength(ExclusiveContext* cx, SourceBufferHolder& srcBuf) - { - // Note this limit is simply so we can store sourceStart and sourceEnd in - // JSScript as 32-bits. It could be lifted fairly easily, since the compiler - // is using size_t internally already. - if (srcBuf.length() > UINT32_MAX) { - if (cx->isJSContext()) -@@ -218,24 +238,17 @@ frontend::CompileScript(ExclusiveContext - JSString* source_ /* = nullptr */, - unsigned staticLevel /* = 0 */, - SourceCompressionTask* extraSct /* = nullptr */) - { - MOZ_ASSERT(srcBuf.get()); - - RootedString source(cx, source_); - -- js::TraceLoggerThread* logger = nullptr; -- if (cx->isJSContext()) -- logger = TraceLoggerForMainThread(cx->asJSContext()->runtime()); -- else -- logger = TraceLoggerForCurrentThread(); -- js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options); -- js::AutoTraceLog scriptLogger(logger, event); -- js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileScript); -+ AutoCompilationTraceLogger traceLogger(cx, TraceLogger_ParserCompileScript); - - /* - * The scripted callerFrame can only be given for compile-and-go scripts - * and non-zero static level requires callerFrame. - */ - MOZ_ASSERT_IF(evalCaller, options.isRunOnce); - MOZ_ASSERT_IF(evalCaller, options.forEval); - MOZ_ASSERT_IF(evalCaller && evalCaller->strict(), options.strictOption); -@@ -467,20 +480,17 @@ frontend::CompileLazyFunction(JSContext* - - CompileOptions options(cx, lazy->version()); - options.setMutedErrors(lazy->mutedErrors()) - .setFileAndLine(lazy->filename(), lazy->lineno()) - .setColumn(lazy->column()) - .setNoScriptRval(false) - .setSelfHostingMode(false); - -- js::TraceLoggerThread* logger = js::TraceLoggerForMainThread(cx->runtime()); -- js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options); -- js::AutoTraceLog scriptLogger(logger, event); -- js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileLazy); -+ AutoCompilationTraceLogger traceLogger(cx, TraceLogger_ParserCompileLazy); - - Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length, - /* foldConstants = */ true, nullptr, lazy); - if (!parser.checkOptions()) - return false; - - uint32_t staticLevel = lazy->staticLevel(cx); - -@@ -534,20 +544,17 @@ frontend::CompileLazyFunction(JSContext* - // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. - static bool - CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyCompileOptions& options, - const AutoNameVector& formals, SourceBufferHolder& srcBuf, - HandleObject enclosingStaticScope, GeneratorKind generatorKind) - { - MOZ_ASSERT(!options.isRunOnce); - -- js::TraceLoggerThread* logger = js::TraceLoggerForMainThread(cx->runtime()); -- js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options); -- js::AutoTraceLog scriptLogger(logger, event); -- js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileFunction); -+ AutoCompilationTraceLogger traceLogger(cx, TraceLogger_ParserCompileFunction); - - // FIXME: make Function pass in two strings and parse them as arguments and - // ProgramElements respectively. - - if (!CheckLength(cx, srcBuf)) - return false; - - RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
diff --git a/src/third_party/mozjs-45/js/src/gc/Marking.cpp.rej b/src/third_party/mozjs-45/js/src/gc/Marking.cpp.rej deleted file mode 100644 index 07083c5..0000000 --- a/src/third_party/mozjs-45/js/src/gc/Marking.cpp.rej +++ /dev/null
@@ -1,83 +0,0 @@ ---- Marking.cpp -+++ Marking.cpp -@@ -587,68 +587,70 @@ DispatchToTracer(JSTracer* trc, T* thing - DoCallback(trc->asCallbackTracer(), thingp, name); - } - - - /*** GC Marking Interface *************************************************************************/ - - namespace js { - -+typedef bool DoNothingMarkingType; -+ - template <typename T> - struct LinearlyMarkedEphemeronKeyType { -- typedef bool Type; // used to instantiate a do-nothing markPotentialEphemeronKeyHelper -+ typedef DoNothingMarkingType Type; - }; - - // For now, we only handle JSObject* keys, but the linear time algorithm can be --// easily extended by adding in more types here, then having them call --// markPotentialEphemeronKey in their GCMarker::traverse instantiation. -+// easily extended by adding in more types here, then instantiating a -+// GCMarker::traverse method that calls markPotentialEphemeronKey. - template <> - struct LinearlyMarkedEphemeronKeyType<JSObject*> { - typedef JSObject* Type; - }; - - // Iterate over the vector. It should not be appended to during iteration - // because the key is already marked, and even in cases where we have a - // multipart key, we should only be inserting entries for the unmarked - // portions. - void --GCMarker::markEphemeronValues(gc::Cell* oldKey, WeakEntryVector& values) -+GCMarker::markEphemeronValues(gc::Cell* markedCell, WeakEntryVector& values) - { - size_t initialLen = values.length(); - for (size_t i = 0; i < initialLen; i++) -- values[i]->maybeMarkEntry(this, oldKey); -+ values[i].weakmap->maybeMarkEntry(this, markedCell, values[i].key); - MOZ_ASSERT(values.length() == initialLen); - } - - template <typename T> - void --GCMarker::markPotentialEphemeronKeyHelper(T oldThing) -+GCMarker::markPotentialEphemeronKeyHelper(T markedThing) - { - if (!isWeakMarkingTracer()) - return; - -- auto weakValues = weakKeys.get(JS::GCCellPtr(oldThing)); -+ auto weakValues = weakKeys.get(JS::GCCellPtr(markedThing)); - if (!weakValues) - return; - -- markEphemeronValues(oldThing, weakValues->value); -+ markEphemeronValues(markedThing, weakValues->value); - weakValues->value.clear(); // If key address is reused, it should do nothing - } - - template <> - void - GCMarker::markPotentialEphemeronKeyHelper(bool) - { - } - - template <typename T> - void --GCMarker::markPotentialEphemeronKey(T* oldThing) -+GCMarker::markPotentialEphemeronKey(T* thing) - { -- markPotentialEphemeronKeyHelper<typename LinearlyMarkedEphemeronKeyType<T*>::Type>(oldThing); -+ markPotentialEphemeronKeyHelper<typename LinearlyMarkedEphemeronKeyType<T*>::Type>(thing); - } - - } // namespace js - - template <typename T> - static inline bool - MustSkipMarking(T thing) - {
diff --git a/src/third_party/mozjs-45/js/src/gc/Marking.h.rej b/src/third_party/mozjs-45/js/src/gc/Marking.h.rej deleted file mode 100644 index 97cb72c..0000000 --- a/src/third_party/mozjs-45/js/src/gc/Marking.h.rej +++ /dev/null
@@ -1,89 +0,0 @@ ---- Marking.h -+++ Marking.h -@@ -11,16 +11,17 @@ - #include "mozilla/HashFunctions.h" - - #include "jsfriendapi.h" - - #include "ds/OrderedHashTable.h" - #include "gc/Heap.h" - #include "gc/Tracer.h" - #include "js/GCAPI.h" -+#include "js/HeapAPI.h" - #include "js/SliceBudget.h" - #include "js/TracingAPI.h" - - class JSLinearString; - class JSRope; - namespace js { - class BaseShape; - class GCMarker; -@@ -141,17 +142,27 @@ namespace gc { - struct WeakKeyTableHashPolicy { - typedef JS::GCCellPtr Lookup; - static HashNumber hash(const Lookup& v) { return mozilla::HashGeneric(v.asCell()); } - static bool match(const JS::GCCellPtr& k, const Lookup& l) { return k == l; } - static bool isEmpty(const JS::GCCellPtr& v) { return !v; } - static void makeEmpty(JS::GCCellPtr* vp) { *vp = nullptr; } - }; - --typedef Vector<WeakMapBase*, 2, js::SystemAllocPolicy> WeakEntryVector; -+struct WeakMarkable { -+ WeakMapBase* weakmap; -+ JS::GCCellPtr key; -+ -+ WeakMarkable(WeakMapBase* weakmapArg, JS::GCCellPtr&& keyArg) -+ : weakmap(weakmapArg), key(keyArg) {} -+ WeakMarkable(WeakMapBase* weakmapArg, JS::GCCellPtr keyArg) -+ : weakmap(weakmapArg), key(keyArg) {} -+}; -+ -+typedef Vector<WeakMarkable, 2, js::SystemAllocPolicy> WeakEntryVector; - - typedef OrderedHashMap<JS::GCCellPtr, - WeakEntryVector, - WeakKeyTableHashPolicy, - js::SystemAllocPolicy> WeakKeyTable; - - } /* namespace gc */ - -@@ -203,16 +214,20 @@ class GCMarker : public JSTracer - } - - void endWeakMarkingPhase() { - MOZ_ASSERT_IF(weakMapAction() == ExpandWeakMaps, tag_ == TracerKindTag::WeakMarking); - tag_ = TracerKindTag::Marking; - weakKeys.clear(); - } - -+ void abortLinearWeakMarking() { -+ endWeakMarkingPhase(); -+ } -+ - void delayMarkingArena(gc::ArenaHeader* aheader); - void delayMarkingChildren(const void* thing); - void markDelayedChildren(gc::ArenaHeader* aheader); - bool markDelayedChildren(SliceBudget& budget); - bool hasDelayedChildren() const { - return !!unmarkedArenaStackTop; - } - -@@ -228,17 +243,17 @@ class GCMarker : public JSTracer - - #ifdef DEBUG - bool shouldCheckCompartments() { return strictCompartmentChecking; } - #endif - - /* This is public exclusively for ScanRope. */ - MarkStack stack; - -- void markEphemeronValues(gc::Cell* oldKey, gc::WeakEntryVector& entry); -+ void markEphemeronValues(gc::Cell* markedCell, gc::WeakEntryVector& entry); - - /* - * Mapping from not yet marked keys to a vector of all values that the key - * maps to in any live weak map. - */ - gc::WeakKeyTable weakKeys; - - private:
diff --git a/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-01.js.rej b/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-01.js.rej deleted file mode 100644 index 55e21a6..0000000 --- a/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-01.js.rej +++ /dev/null
@@ -1,40 +0,0 @@ ---- weak-marking-01.js -+++ weak-marking-01.js -@@ -85,15 +85,35 @@ function deadKeys() { - - gc(); - assertEq(finalizeCount(), initialCount + 2); - assertEq(nondeterministicGetWeakMapKeys(wm1).length, 0); - } - - deadKeys(); - -+// The weakKeys table has to grow if it encounters enough new unmarked weakmap -+// keys. Trigger this to happen during weakmap marking. -+// -+// There's some trickiness involved in getting it to test the right thing, -+// because if a key is marked before the weakmap, then it won't get entered -+// into the weakKeys table. This chains through multiple weakmap layers to -+// ensure that the objects can't get marked before the weakmaps. - function weakKeysRealloc() { - var wm1 = new WeakMap; -+ var wm2 = new WeakMap; -+ var wm3 = new WeakMap; - var obj1 = {'name': 'obj1'}; -- var wm2 = new WeakMap; -+ var obj2 = {'name': 'obj2'}; - wm1.set(obj1, wm2); -- -+ wm2.set(obj2, wm3); -+ for (var i = 0; i < 10000; i++) { -+ wm3.set(Object.create(null), wm2); -+ } -+ wm3.set(Object.create(null), makeFinalizeObserver()); -+ wm2 = undefined; -+ wm3 = undefined; -+ obj2 = undefined; -+ -+ var initialCount = finalizeCount(); -+ gc(); -+ assertEq(finalizeCount(), initialCount + 1); - }
diff --git a/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-02.js.rej b/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-02.js.rej deleted file mode 100644 index cd65b0e..0000000 --- a/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-02.js.rej +++ /dev/null
@@ -1,66 +0,0 @@ ---- weak-marking-02.js -+++ weak-marking-02.js -@@ -0,0 +1,63 @@ -+// These tests will be using object literals as keys, and we want some of them -+// to be dead after being inserted into a WeakMap. That means we must wrap -+// everything in functions because it seems like the toplevel script hangs onto -+// its object literals. -+ -+// Cross-compartment WeakMap keys work by storing a cross-compartment wrapper -+// in the WeakMap, and the actual "delegate" object in the target compartment -+// is the thing whose liveness is checked. -+ -+var g2 = newGlobal(); -+g2.eval('function genObj(name) { return {"name": name} }'); -+ -+function basicSweeping() { -+ var wm1 = new WeakMap(); -+ wm1.set({'name': 'obj1'}, {'name': 'val1'}); -+ var hold = g2.genObj('obj2'); -+ wm1.set(hold, {'name': 'val2'}); -+ wm1.set({'name': 'obj3'}, {'name': 'val3'}); -+ var obj4 = g2.genObj('obj4'); -+ wm1.set(obj4, {'name': 'val3'}); -+ obj4 = undefined; -+ -+ gc(); -+ assertEq(wm1.get(hold).name, 'val2'); -+ assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1); -+} -+ -+basicSweeping(); -+ -+// Same, but behind an additional WM layer, to avoid ordering problems (not -+// that I've checked that basicSweeping even has any problems.) -+function basicSweeping2() { -+ var wm1 = new WeakMap(); -+ wm1.set({'name': 'obj1'}, {'name': 'val1'}); -+ var hold = g2.genObj('obj2'); -+ wm1.set(hold, {'name': 'val2'}); -+ wm1.set({'name': 'obj3'}, {'name': 'val3'}); -+ var obj4 = g2.genObj('obj4'); -+ wm1.set(obj4, {'name': 'val3'}); -+ obj4 = undefined; -+ -+ var base1 = {'name': 'base1'}; -+ var base2 = {'name': 'base2'}; -+ var wm_base1 = new WeakMap(); -+ var wm_base2 = new WeakMap(); -+ wm_base1.set(base1, wm_base2); -+ wm_base2.set(base2, wm1); -+ wm1 = wm_base2 = undefined; -+ -+ gc(); -+ -+ assertEq(nondeterministicGetWeakMapKeys(wm_base1).length, 1); -+ wm_base2 = wm_base1.get(base1); -+ assertEq(nondeterministicGetWeakMapKeys(wm_base2).length, 1); -+ assertEq(nondeterministicGetWeakMapKeys(wm_base1)[0], base1); -+ assertEq(nondeterministicGetWeakMapKeys(wm_base2)[0], base2); -+ wm_base2 = wm_base1.get(base1); -+ wm1 = wm_base2.get(base2); -+ assertEq(wm1.get(hold).name, 'val2'); -+ assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1); -+} -+ -+basicSweeping2();
diff --git a/src/third_party/mozjs-45/js/src/jsweakmap.h.rej b/src/third_party/mozjs-45/js/src/jsweakmap.h.rej deleted file mode 100644 index bcd1675..0000000 --- a/src/third_party/mozjs-45/js/src/jsweakmap.h.rej +++ /dev/null
@@ -1,124 +0,0 @@ ---- jsweakmap.h -+++ jsweakmap.h -@@ -78,17 +78,17 @@ class WeakMapBase { - - // Restore information about which weak maps are marked for many compartments. - static void restoreCompartmentMarkedWeakMaps(WeakMapSet& markedWeakMaps); - - // Remove a weakmap from its compartment's weakmaps list. - static void removeWeakMapFromList(WeakMapBase* weakmap); - - // Object keys must participate in ephemeron marking by overriding this method. -- virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* l) = 0; -+ virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr l) = 0; - - protected: - // Instance member functions called by the above. Instantiations of WeakMap override - // these with definitions appropriate for their Key and Value types. - virtual void nonMarkingTraceKeys(JSTracer* tracer) = 0; - virtual void nonMarkingTraceValues(JSTracer* tracer) = 0; - virtual bool markIteratively(JSTracer* tracer) = 0; - virtual void markEphemeronEntries(JSTracer* trc) = 0; -@@ -160,65 +160,76 @@ class WeakMap : public HashMap<Key, Valu - return p; - } - - // The WeakMap and some part of the key are marked. Mark the value if the - // key is "fully marked" according to the exact semantics of this WeakMap. - // (For a standard WeakMap, the key is either marked or not, so just mark - // the value here. But a subclass might have multipart keys with more - // complicated semantics.) -- virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* l) -+ virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr origKey) - { - MOZ_ASSERT(marked); - -+ gc::Cell* l = origKey.asCell(); - Ptr p = Base::lookup(reinterpret_cast<const Lookup&>(l)); - Key key(p->key()); -- if (gc::IsMarked(&key)) -+ if (gc::IsMarked(&key)) { - TraceEdge(trc, &p->value(), "ephemeron value"); -+ } else if (keyNeedsMark(key)) { -+ TraceEdge(trc, &p->value(), "WeakMap ephemeron value"); -+ TraceEdge(trc, &key, "proxy-preserved WeakMap ephemeron key"); -+ MOZ_ASSERT(key == p->key()); // No moving -+ } - key.unsafeSet(nullptr); - } - - protected: -+ static void addWeakEntry(JSTracer* trc, JS::GCCellPtr key, gc::WeakMarkable markable) -+ { -+ GCMarker& marker = *static_cast<GCMarker*>(trc); -+ -+ auto p = marker.weakKeys.get(key); -+ if (p) { -+ gc::WeakEntryVector& weakEntries = p->value; -+ if (!weakEntries.append(Move(markable))) -+ marker.abortLinearWeakMarking(); -+ } else { -+ gc::WeakEntryVector weakEntries; -+ JS_ALWAYS_TRUE(weakEntries.append(Move(markable))); -+ if (!marker.weakKeys.put(JS::GCCellPtr(key), Move(weakEntries))) -+ marker.abortLinearWeakMarking(); -+ } -+ } -+ - virtual void markEphemeronEntries(JSTracer* trc) { -- gc::WeakKeyTable& weakKeys = static_cast<GCMarker*>(trc)->weakKeys; -- - MOZ_ASSERT(marked); - for (Enum e(*this); !e.empty(); e.popFront()) { - Key key(e.front().key()); - - // If the entry is live, ensure its key and value are marked. - if (gc::IsMarked(&key)) { - (void) markValue(trc, &e.front().value()); -- if (e.front().key() != key) -- entryMoved(e, key); -+ MOZ_ASSERT(key == e.front().key()); // No moving - } else if (keyNeedsMark(key)) { - TraceEdge(trc, &e.front().value(), "WeakMap entry value"); - TraceEdge(trc, &key, "proxy-preserved WeakMap entry key"); -- if (e.front().key() != key) -- entryMoved(e, key); -+ MOZ_ASSERT(key == e.front().key()); // No moving - } else { - // Entry is not known to be live yet. Record it in the list of -- // weak keys. Or rather, record a pointer to this weakmap, so -- // we can look the key up again when we need to (to allow -- // incremental weak marking.) -- auto p = weakKeys.get(JS::GCCellPtr(key)); -- if (p) { -- gc::WeakEntryVector& weakEntries = p->value; -- if (!weakEntries.append(this)) { -- static_cast<GCMarker*>(trc)->endWeakMarkingPhase(); -- break; -- } -- } else { -- gc::WeakEntryVector weakEntries; -- JS_ALWAYS_TRUE(weakEntries.append(this)); -- if (!weakKeys.put(JS::GCCellPtr(key), Move(weakEntries))) { -- static_cast<GCMarker*>(trc)->endWeakMarkingPhase(); -- break; -- } -- } -+ // weak keys. Or rather, record this weakmap and the lookup key -+ // so we can repeat the lookup when we need to (to allow -+ // incremental weak marking, we can't just store a pointer to -+ // the entry.) Also record the delegate, if any, because -+ // marking the delegate must also mark the entry. -+ JS::GCCellPtr weakKey(key); -+ gc::WeakMarkable markable(this, weakKey); -+ addWeakEntry(trc, weakKey, markable); -+ if (JSObject* delegate = getDelegate(key)) -+ addWeakEntry(trc, JS::GCCellPtr(delegate), markable); - } - key.unsafeSet(nullptr); - } - } - - private: - void exposeGCThingToActiveJS(const JS::Value& v) const { JS::ExposeValueToActiveJS(v); } - void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
diff --git a/src/third_party/mozjs-45/js/src/shell/OSObject.cppTNHQfd b/src/third_party/mozjs-45/js/src/shell/OSObject.cppTNHQfd deleted file mode 100644 index 4744d38..0000000 --- a/src/third_party/mozjs-45/js/src/shell/OSObject.cppTNHQfd +++ /dev/null
@@ -1,443 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// OSObject.h - os object for exposing posix system calls in the JS shell - -#include "shell/OSObject.h" - -#include <errno.h> -#include <stdlib.h> -#ifdef XP_WIN -#include <direct.h> -#include <process.h> -#include <string.h> -#else -#include <sys/wait.h> -#include <unistd.h> -#endif - -#include "jsapi.h" -// For JSFunctionSpecWithHelp -#include "jsfriendapi.h" -#include "jsobj.h" -#ifdef XP_WIN -# include "jswin.h" -#endif -#include "jswrapper.h" - -#include "js/Conversions.h" -#include "shell/jsshell.h" -#include "vm/TypedArrayObject.h" - -#include "jsobjinlines.h" - -#ifdef XP_WIN -# define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR) -# define getcwd _getcwd -#else -# include <libgen.h> -#endif - -using js::shell::RCFile; - -static RCFile** gErrFilePtr = nullptr; -static RCFile** gOutFilePtr = nullptr; - -using namespace JS; - -namespace js { -namespace shell { - -/* - * Resolve a (possibly) relative filename to an absolute path. If - * |scriptRelative| is true, then the result will be relative to the directory - * containing the currently-running script, or the current working directory if - * the currently-running script is "-e" (namely, you're using it from the - * command line.) Otherwise, it will be relative to the current working - * directory. - */ -JSString* -ResolvePath(JSContext* cx, HandleString filenameStr, PathResolutionMode resolveMode) -{ - JSAutoByteString filename(cx, filenameStr); - if (!filename) - return nullptr; - - const char* pathname = filename.ptr(); - if (pathname[0] == '/') - return filenameStr; -#ifdef XP_WIN - // Various forms of absolute paths per http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx - // "\..." - if (pathname[0] == '\\') - return filenameStr; - // "C:\..." - if (strlen(pathname) > 3 && isalpha(pathname[0]) && pathname[1] == ':' && pathname[2] == '\\') - return filenameStr; - // "\\..." - if (strlen(pathname) > 2 && pathname[1] == '\\' && pathname[2] == '\\') - return filenameStr; -#endif - - /* Get the currently executing script's name. */ - JS::AutoFilename scriptFilename; - if (!DescribeScriptedCaller(cx, &scriptFilename)) - return nullptr; - - if (!scriptFilename.get()) - return nullptr; - - if (strcmp(scriptFilename.get(), "-e") == 0 || strcmp(scriptFilename.get(), "typein") == 0) - resolveMode = RootRelative; - - static char buffer[PATH_MAX+1]; - if (resolveMode == ScriptRelative) { -#ifdef XP_WIN - // The docs say it can return EINVAL, but the compiler says it's void - _splitpath(scriptFilename.get(), nullptr, buffer, nullptr, nullptr); -#else - strncpy(buffer, scriptFilename.get(), PATH_MAX+1); - if (buffer[PATH_MAX] != '\0') - return nullptr; - - // dirname(buffer) might return buffer, or it might return a - // statically-allocated string - memmove(buffer, dirname(buffer), strlen(buffer) + 1); -#endif - } else { - const char* cwd = getcwd(buffer, PATH_MAX); - if (!cwd) - return nullptr; - } - - size_t len = strlen(buffer); - buffer[len] = '/'; - strncpy(buffer + len + 1, pathname, sizeof(buffer) - (len+1)); - if (buffer[PATH_MAX] != '\0') - return nullptr; - - return JS_NewStringCopyZ(cx, buffer); -} - -static JSObject* -FileAsTypedArray(JSContext* cx, const char* pathname) -{ - FILE* file = fopen(pathname, "rb"); - if (!file) { - JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno)); - return nullptr; - } - AutoCloseInputFile autoClose(file); - - RootedObject obj(cx); - if (fseek(file, 0, SEEK_END) != 0) { - JS_ReportError(cx, "can't seek end of %s", pathname); - } else { - size_t len = ftell(file); - if (fseek(file, 0, SEEK_SET) != 0) { - JS_ReportError(cx, "can't seek start of %s", pathname); - } else { - obj = JS_NewUint8Array(cx, len); - if (!obj) - return nullptr; - char* buf = (char*) obj->as<js::TypedArrayObject>().viewData(); - size_t cc = fread(buf, 1, len, file); - if (cc != len) { - JS_ReportError(cx, "can't read %s: %s", pathname, - (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read"); - obj = nullptr; - } - } - } - return obj; -} - -static bool -ReadFile(JSContext* cx, unsigned argc, jsval* vp, bool scriptRelative) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - if (args.length() < 1 || args.length() > 2) { - JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr, - args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, - "snarf"); - return false; - } - - if (!args[0].isString() || (args.length() == 2 && !args[1].isString())) { - JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "snarf"); - return false; - } - - RootedString givenPath(cx, args[0].toString()); - RootedString str(cx, js::shell::ResolvePath(cx, givenPath, scriptRelative ? ScriptRelative : RootRelative)); - if (!str) - return false; - - JSAutoByteString filename(cx, str); - if (!filename) - return false; - - if (args.length() > 1) { - JSString* opt = JS::ToString(cx, args[1]); - if (!opt) - return false; - bool match; - if (!JS_StringEqualsAscii(cx, opt, "binary", &match)) - return false; - if (match) { - JSObject* obj; - if (!(obj = FileAsTypedArray(cx, filename.ptr()))) - return false; - args.rval().setObject(*obj); - return true; - } - } - - if (!(str = FileAsString(cx, filename.ptr()))) - return false; - args.rval().setString(str); - return true; -} - -static bool -osfile_readFile(JSContext* cx, unsigned argc, jsval* vp) -{ - return ReadFile(cx, argc, vp, false); -} - -static bool -osfile_readRelativeToScript(JSContext* cx, unsigned argc, jsval* vp) -{ - return ReadFile(cx, argc, vp, true); -} - -static bool -Redirect(JSContext* cx, FILE* fp, HandleString relFilename) -{ - RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative)); - if (!filename) - return false; - JSAutoByteString filenameABS(cx, filename); - if (!filenameABS) - return false; - if (freopen(filenameABS.ptr(), "wb", fp) == nullptr) { - JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno)); - return false; - } - return true; -} - -static bool -osfile_redirect(JSContext* cx, unsigned argc, jsval* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - if (args.length() < 1 || args.length() > 2) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect"); - return false; - } - - if (args[0].isString()) { - RootedString stdoutPath(cx, args[0].toString()); - if (!stdoutPath) - return false; - if (!Redirect(cx, stdout, stdoutPath)) - return false; - } - - if (args.length() > 1 && args[1].isString()) { - RootedString stderrPath(cx, args[1].toString()); - if (!stderrPath) - return false; - if (!Redirect(cx, stderr, stderrPath)) - return false; - } - - args.rval().setUndefined(); - return true; -} -<<<<<<< - -||||||| -======= - -/* static */ RCFile* -RCFile::create(JSContext* cx, const char* filename, const char* mode) -{ - FILE* fp = fopen(filename, mode); - if (!fp) - return nullptr; - - RCFile* file = cx->new_<RCFile>(fp); - if (!file) { - fclose(fp); - return nullptr; - } - - return file; -} - -void -RCFile::close() -{ - if (fp) - fclose(fp); - fp = nullptr; -} - -bool -RCFile::release() -{ - if (--numRefs) - return false; - this->close(); - return true; -} - -class FileObject : public JSObject { - enum : uint32_t { - FILE_SLOT = 0, - NUM_SLOTS - }; - - public: - static const js::Class class_; - - static FileObject* create(JSContext* cx, RCFile* file) { - JSObject* obj = js::NewObjectWithClassProto(cx, &class_, JS::NullPtr()); - if (!obj) - return nullptr; - - FileObject* fileObj = &obj->as<FileObject>(); - fileObj->setRCFile(file); - file->acquire(); - return fileObj; - } - - static void finalize(FreeOp* fop, JSObject* obj) { - FileObject* fileObj = &obj->as<FileObject>(); - RCFile* file = fileObj->rcFile(); - if (file->release()) { - fileObj->setRCFile(nullptr); - fop->delete_(file); - } - } - - bool isOpen() { - RCFile* file = rcFile(); - return file && file->isOpen(); - } - - void close() { - if (!isOpen()) - return; - rcFile()->close(); - } - - RCFile* rcFile() { - return reinterpret_cast<RCFile*>(js::GetReservedSlot(this, FILE_SLOT).toPrivate()); - } - - private: - - void setRCFile(RCFile* file) { - js::SetReservedSlot(this, FILE_SLOT, PrivateValue(file)); - } -}; - -const js::Class FileObject::class_ = { - "File", - JSCLASS_HAS_RESERVED_SLOTS(FileObject::NUM_SLOTS), - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - nullptr, /* convert */ - FileObject::finalize, /* finalize */ - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - nullptr /* trace */ -}; - -static FileObject* -redirect(JSContext* cx, HandleString relFilename, RCFile** globalFile) -{ - RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative)); - if (!filename) - return nullptr; - JSAutoByteString filenameABS(cx, filename); - if (!filenameABS) - return nullptr; - RCFile* file = RCFile::create(cx, filenameABS.ptr(), "wb"); - if (!file) { - JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno)); - return nullptr; - } - - // The global gOutFile acquires ownership of the new file, releases - // ownership of its old file, and we return a FileObject owning the old - // file. - file->acquire(); // Global owner of new file - - FileObject* fileObj = FileObject::create(cx, *globalFile); // Newly created owner of old file - if (!fileObj) { - file->release(); - return nullptr; - } - - (*globalFile)->release(); // Release (global) ownership of old file. - *globalFile = file; - - return fileObj; -} - -static bool -Redirect(JSContext* cx, const CallArgs& args, RCFile** outFile) -{ - if (args.length() > 1) { - JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect"); - return false; - } - - RCFile* oldFile = *outFile; - RootedObject oldFileObj(cx, FileObject::create(cx, oldFile)); - if (!oldFileObj) - return false; - - if (args.get(0).isUndefined()) { - args.rval().setObject(*oldFileObj); - return true; - } - - if (args[0].isObject()) { - RootedObject fileObj(cx, js::CheckedUnwrap(&args[0].toObject())); - if (!fileObj) - return false; - if (fileObj->is<FileObject>()) { - // Passed in a FileObject. Create a FileObject for the previous - // global file, and set the global file to the passed-in one. - *outFile = fileObj->as<FileObject>().rcFile(); - (*outFile)->acquire(); - oldFile->release(); - - args.rval().setObject(*oldFileObj); - return true; - } - } - - RootedString filename(cx, JS::ToString(cx, args[0])); - if (!filename) - return false; - - if (!redirect(cx, filename, outFile)) - return false; - - args.rv \ No newline at end of file
diff --git a/src/third_party/mozjs-45/js/src/shell/jsshell.h.rej b/src/third_party/mozjs-45/js/src/shell/jsshell.h.rej deleted file mode 100644 index 217a0f6..0000000 --- a/src/third_party/mozjs-45/js/src/shell/jsshell.h.rej +++ /dev/null
@@ -1,43 +0,0 @@ ---- jsshell.h -+++ jsshell.h -@@ -45,30 +49,30 @@ class AutoCloseFile - bool success = true; - if (f_ && f_ != stdin && f_ != stdout && f_ != stderr) - success = !fclose(f_); - f_ = nullptr; - return success; - } - }; - --// Reference counted file. --struct RCFile { -+struct File final : public RefCounted<File> { - FILE* fp; -- uint32_t numRefs; - -- RCFile() : fp(nullptr), numRefs(0) {} -- RCFile(FILE* fp) : fp(fp), numRefs(0) {} -+ File() : fp(nullptr) {} -+ File(FILE* fp) : fp(fp) {} -+ ~File() { -+ this->close(); -+ } - -- void acquire() { numRefs++; } -- -- // Starts out with a ref count of zero. -- static RCFile* create(JSContext* cx, const char* filename, const char* mode); -+ static already_AddRefed<File> -+ create(JSContext* cx, const char* filename, const char* mode); - - void close(); - bool isOpen() const { return fp; } -- bool release(); - }; - -+using RCFile = RefPtr<File>; -+ - } /* namespace shell */ - } /* namespace js */ - - #endif
diff --git a/src/third_party/mozjs-45/js/src/vm/UbiNodeCensus.cpp.rej b/src/third_party/mozjs-45/js/src/vm/UbiNodeCensus.cpp.rej deleted file mode 100644 index a54806a..0000000 --- a/src/third_party/mozjs-45/js/src/vm/UbiNodeCensus.cpp.rej +++ /dev/null
@@ -1,23 +0,0 @@ ---- UbiNodeCensus.cpp -+++ UbiNodeCensus.cpp -@@ -1,16 +1,20 @@ - /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - #include "js/UbiNodeCensus.h" - -+#include "jscntxt.h" -+#include "jscompartment.h" -+#include "jsobjinlines.h" -+ - using namespace js; - - namespace JS { - namespace ubi { - - void - CountDeleter::operator()(CountBase* ptr) - {
diff --git a/src/third_party/musl/musl.gyp b/src/third_party/musl/musl.gyp index 895e07f..2a85789 100644 --- a/src/third_party/musl/musl.gyp +++ b/src/third_party/musl/musl.gyp
@@ -55,7 +55,7 @@ # no-unknown-pragmas -w shift-op-parentheses '-nobuiltininc', '-isystem<(DEPTH)/third_party/musl/include', - '-isystem<(DEPTH)/third_party/musl/arch/x86_64', + '-isystem<(DEPTH)/third_party/musl/arch/<(musl_arch)', '-isystem<(DEPTH)/third_party/musl/arch/generic', '-w', ],
diff --git a/src/third_party/woff2/woff2.gyp b/src/third_party/woff2/woff2.gyp index 68434ac..deca5f9 100644 --- a/src/third_party/woff2/woff2.gyp +++ b/src/third_party/woff2/woff2.gyp
@@ -3,6 +3,9 @@ # found in the LICENSE file. { + 'variables': { + 'optimize_target_for_speed': 1, + }, 'targets': [ { 'target_name': 'woff2_dec',
diff --git a/src/updater_components/bsdiff.h b/src/updater_components/bsdiff.h new file mode 100644 index 0000000..c1d26c7 --- /dev/null +++ b/src/updater_components/bsdiff.h
@@ -0,0 +1,107 @@ +// Copyright 2003, 2004 Colin Percival +// All rights reserved +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted providing that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// For the terms under which this work may be distributed, please see +// the adjoining file "LICENSE". +// +// Changelog: +// 2005-04-26 - Define the header as a C structure, add a CRC32 checksum to +// the header, and make all the types 32-bit. +// --Benjamin Smedberg <benjamin@smedbergs.us> +// 2009-03-31 - Change to use Streams. Move CRC code to crc.{h,cc} +// Changed status to an enum, removed unused status codes. +// --Stephen Adams <sra@chromium.org> +// 2013-04-10 - Added wrapper to apply a patch directly to files. +// --Joshua Pawlicki <waffles@chromium.org> + +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COURGETTE_THIRD_PARTY_BSDIFF_BSDIFF_H_ +#define COURGETTE_THIRD_PARTY_BSDIFF_BSDIFF_H_ + +#include <stdint.h> + +#include "base/files/file.h" +#include "base/files/file_util.h" + +namespace courgette { +class SourceStream; +class SinkStream; +} // namespace courgette + +namespace bsdiff { + +enum BSDiffStatus { + OK = 0, + MEM_ERROR = 1, + CRC_ERROR = 2, + READ_ERROR = 3, + UNEXPECTED_ERROR = 4, + WRITE_ERROR = 5 +}; + +// Creates a binary patch. +// +BSDiffStatus CreateBinaryPatch(courgette::SourceStream* old_stream, + courgette::SourceStream* new_stream, + courgette::SinkStream* patch_stream); + +// Applies the given patch file to a given source file. This method validates +// the CRC of the original file stored in the patch file, before applying the +// patch to it. +// +BSDiffStatus ApplyBinaryPatch(courgette::SourceStream* old_stream, + courgette::SourceStream* patch_stream, + courgette::SinkStream* new_stream); + +// As above, but simply takes base::Files. +BSDiffStatus ApplyBinaryPatch(base::File old_stream, + base::File patch_stream, + base::File new_stream); + +// As above, but simply takes the file paths. +BSDiffStatus ApplyBinaryPatch(const base::FilePath& old_stream, + const base::FilePath& patch_stream, + const base::FilePath& new_stream); + +// The following declarations are common to the patch-creation and +// patch-application code. + +// The patch stream starts with a MBSPatchHeader. +typedef struct MBSPatchHeader_ { + char tag[8]; // Contains MBS_PATCH_HEADER_TAG. + uint32_t slen; // Length of the file to be patched. + uint32_t scrc32; // CRC32 of the file to be patched. + uint32_t dlen; // Length of the result file. +} MBSPatchHeader; + +// This is the value for the tag field. Must match length exactly, not counting +// null at end of string. +#define MBS_PATCH_HEADER_TAG "GBSDIF42" + +} // namespace bsdiff + +#endif // COURGETTE_THIRD_PARTY_BSDIFF_BSDIFF_H_
diff --git a/src/updater_components/client_update_protocol/BUILD.gn b/src/updater_components/client_update_protocol/BUILD.gn new file mode 100644 index 0000000..e22ee24 --- /dev/null +++ b/src/updater_components/client_update_protocol/BUILD.gn
@@ -0,0 +1,29 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +static_library("client_update_protocol") { + sources = [ + "ecdsa.cc", + "ecdsa.h", + ] + + deps = [ + "//base", + "//crypto", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "ecdsa_unittest.cc", + ] + + deps = [ + ":client_update_protocol", + "//base", + "//crypto", + "//testing/gtest", + ] +}
diff --git a/src/updater_components/client_update_protocol/ecdsa.cc b/src/updater_components/client_update_protocol/ecdsa.cc new file mode 100644 index 0000000..53b7e90 --- /dev/null +++ b/src/updater_components/client_update_protocol/ecdsa.cc
@@ -0,0 +1,188 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/client_update_protocol/ecdsa.h" + +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "crypto/random.h" +#include "crypto/sha2.h" +#include "crypto/signature_verifier.h" + +namespace client_update_protocol { + +namespace { + +std::vector<uint8_t> SHA256HashStr(const base::StringPiece& str) { + std::vector<uint8_t> result(crypto::kSHA256Length); + crypto::SHA256HashString(str, &result.front(), result.size()); + return result; +} + +std::vector<uint8_t> SHA256HashVec(const std::vector<uint8_t>& vec) { + if (vec.empty()) + return SHA256HashStr(base::StringPiece()); + + return SHA256HashStr(base::StringPiece( + reinterpret_cast<const char*>(&vec.front()), vec.size())); +} + +bool ParseETagHeader(const base::StringPiece& etag_header_value_in, + std::vector<uint8_t>* ecdsa_signature_out, + std::vector<uint8_t>* request_hash_out) { + DCHECK(ecdsa_signature_out); + DCHECK(request_hash_out); + + // The ETag value is a UTF-8 string, formatted as "S:H", where: + // * S is the ECDSA signature in DER-encoded ASN.1 form, converted to hex. + // * H is the SHA-256 hash of the observed request body, standard hex format. + // A Weak ETag is formatted as W/"S:H". This function treats it the same as a + // strong ETag. + base::StringPiece etag_header_value(etag_header_value_in); + + // Remove the weak prefix, then remove the begin and the end quotes. + const char kWeakETagPrefix[] = "W/"; + if (etag_header_value.starts_with(kWeakETagPrefix)) + etag_header_value.remove_prefix(base::size(kWeakETagPrefix) - 1); + if (etag_header_value.size() >= 2 && etag_header_value.starts_with("\"") && + etag_header_value.ends_with("\"")) { + etag_header_value.remove_prefix(1); + etag_header_value.remove_suffix(1); + } + + const base::StringPiece::size_type delim_pos = etag_header_value.find(':'); + if (delim_pos == base::StringPiece::npos || delim_pos == 0 || + delim_pos == etag_header_value.size() - 1) + return false; + + const base::StringPiece sig_hex = etag_header_value.substr(0, delim_pos); + const base::StringPiece hash_hex = etag_header_value.substr(delim_pos + 1); + + // Decode the ECDSA signature. Don't bother validating the contents of it; + // the SignatureValidator class will handle the actual DER decoding and + // ASN.1 parsing. Check for an expected size range only -- valid ECDSA + // signatures are between 8 and 72 bytes. + if (!base::HexStringToBytes(sig_hex, ecdsa_signature_out)) + return false; + if (ecdsa_signature_out->size() < 8 || ecdsa_signature_out->size() > 72) + return false; + + // Decode the SHA-256 hash; it should be exactly 32 bytes, no more or less. + if (!base::HexStringToBytes(hash_hex, request_hash_out)) + return false; + if (request_hash_out->size() != crypto::kSHA256Length) + return false; + + return true; +} + +} // namespace + +Ecdsa::Ecdsa(int key_version, const base::StringPiece& public_key) + : pub_key_version_(key_version), + public_key_(public_key.begin(), public_key.end()) {} + +Ecdsa::~Ecdsa() {} + +std::unique_ptr<Ecdsa> Ecdsa::Create(int key_version, + const base::StringPiece& public_key) { + DCHECK_GT(key_version, 0); + DCHECK(!public_key.empty()); + + return base::WrapUnique(new Ecdsa(key_version, public_key)); +} + +void Ecdsa::OverrideNonceForTesting(int key_version, uint32_t nonce) { + DCHECK(!request_query_cup2key_.empty()); + request_query_cup2key_ = base::StringPrintf("%d:%u", pub_key_version_, nonce); +} + +void Ecdsa::SignRequest(const base::StringPiece& request_body, + std::string* query_params) { + DCHECK(query_params); + + // Generate a random nonce to use for freshness, build the cup2key query + // string, and compute the SHA-256 hash of the request body. Set these + // two pieces of data aside to use during ValidateResponse(). + uint32_t nonce = 0; + crypto::RandBytes(&nonce, sizeof(nonce)); + request_query_cup2key_ = base::StringPrintf("%d:%u", pub_key_version_, nonce); + request_hash_ = SHA256HashStr(request_body); + + // Return the query string for the user to send with the request. + std::string request_hash_hex = + base::HexEncode(&request_hash_.front(), request_hash_.size()); + request_hash_hex = base::ToLowerASCII(request_hash_hex); + + *query_params = base::StringPrintf("cup2key=%s&cup2hreq=%s", + request_query_cup2key_.c_str(), + request_hash_hex.c_str()); +} + +bool Ecdsa::ValidateResponse(const base::StringPiece& response_body, + const base::StringPiece& server_etag) { + DCHECK(!request_hash_.empty()); + DCHECK(!request_query_cup2key_.empty()); + + if (response_body.empty() || server_etag.empty()) + return false; + + // Break the ETag into its two components (the ECDSA signature, and the + // hash of the request that the server observed) and decode to byte buffers. + std::vector<uint8_t> signature; + std::vector<uint8_t> observed_request_hash; + if (!ParseETagHeader(server_etag, &signature, &observed_request_hash)) + return false; + + // Check that the server's observed request hash is equal to the original + // request hash. (This is a quick rejection test; the signature test is + // authoritative, but slower.) + DCHECK_EQ(request_hash_.size(), crypto::kSHA256Length); + if (observed_request_hash.size() != crypto::kSHA256Length) + return false; + if (!std::equal(observed_request_hash.begin(), observed_request_hash.end(), + request_hash_.begin())) + return false; + + // Next, build the buffer that the server will have signed on its end: + // hash( hash(request) | hash(response) | cup2key_query_string ) + // When building the client's version of the buffer, it's important to use + // the original request hash that it attempted to send, and not the observed + // request hash that the server sent back to us. + const std::vector<uint8_t> response_hash = SHA256HashStr(response_body); + + std::vector<uint8_t> signed_message; + signed_message.insert(signed_message.end(), request_hash_.begin(), + request_hash_.end()); + signed_message.insert(signed_message.end(), response_hash.begin(), + response_hash.end()); + signed_message.insert(signed_message.end(), request_query_cup2key_.begin(), + request_query_cup2key_.end()); + + const std::vector<uint8_t> signed_message_hash = + SHA256HashVec(signed_message); + + // Initialize the signature verifier. + crypto::SignatureVerifier verifier; + if (!verifier.VerifyInit(crypto::SignatureVerifier::ECDSA_SHA256, signature, + public_key_)) { + DVLOG(1) << "Couldn't init SignatureVerifier."; + return false; + } + + // If the verification fails, that implies one of two outcomes: + // * The signature was modified + // * The buffer that the server signed does not match the buffer that the + // client assembled -- implying that either request body or response body + // was modified, or a different nonce value was used. + verifier.VerifyUpdate(signed_message_hash); + return verifier.VerifyFinal(); +} + +} // namespace client_update_protocol
diff --git a/src/updater_components/client_update_protocol/ecdsa.h b/src/updater_components/client_update_protocol/ecdsa.h new file mode 100644 index 0000000..24e4171 --- /dev/null +++ b/src/updater_components/client_update_protocol/ecdsa.h
@@ -0,0 +1,88 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CLIENT_UPDATE_PROTOCOL_ECDSA_H_ +#define COMPONENTS_CLIENT_UPDATE_PROTOCOL_ECDSA_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/strings/string_piece.h" + +namespace client_update_protocol { + +// Client Update Protocol v2, or CUP-ECDSA, is used by Google Update (Omaha) +// servers to ensure freshness and authenticity of server responses over HTTP, +// without the overhead of HTTPS -- namely, no PKI, no guarantee of privacy, and +// no request replay protection. +// +// CUP-ECDSA relies on a single signing operation using ECDSA with SHA-256, +// instead of the original CUP which used HMAC-SHA1 with a random signing key +// encrypted using RSA. +// +// Each |Ecdsa| object represents a single network ping in flight -- a call to +// SignRequest() generates internal state that will be used by +// ValidateResponse(). +class Ecdsa { + public: + ~Ecdsa(); + + // Initializes this instance of CUP-ECDSA with a versioned public key. + // |key_version| must be non-negative. |public_key| is expected to be a + // DER-encoded ASN.1 SubjectPublicKeyInfo containing an ECDSA public key. + // Returns a NULL pointer on failure. + static std::unique_ptr<Ecdsa> Create(int key_version, + const base::StringPiece& public_key); + + // Generates freshness/authentication data for an outgoing ping. + // |request_body| contains the body of the ping in UTF-8. On return, + // |query_params| contains a set of query parameters (in UTF-8) to be appended + // to the URL. + // + // This method will store internal state in this instance used by calls to + // ValidateResponse(); if you need to have multiple pings in flight, + // initialize a separate CUP-ECDSA instance for each one. + void SignRequest(const base::StringPiece& request_body, + std::string* query_params); + + // Validates a response given to a ping previously signed with + // SignRequest(). |response_body| contains the body of the response in + // UTF-8. |signature| contains the ECDSA signature and observed request + // hash. Returns true if the response is valid and the observed request hash + // matches the sent hash. This method uses internal state that is set by a + // prior SignRequest() call. + bool ValidateResponse(const base::StringPiece& response_body, + const base::StringPiece& signature); + + // Sets the key and nonce that were used to generate a signature that is baked + // into a unit test. + void OverrideNonceForTesting(int key_version, uint32_t nonce); + + private: + Ecdsa(int key_version, const base::StringPiece& public_key); + + // The server keeps multiple signing keys; a version must be sent so that + // the correct signing key is used to sign the assembled message. + const int pub_key_version_; + + // The ECDSA public key to use for verifying response signatures. + const std::vector<uint8_t> public_key_; + + // The SHA-256 hash of the XML request. This is modified on each call to + // SignRequest(), and checked by ValidateResponse(). + std::vector<uint8_t> request_hash_; + + // The query string containing key version and nonce in UTF-8 form. This is + // modified on each call to SignRequest(), and checked by ValidateResponse(). + std::string request_query_cup2key_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(Ecdsa); +}; + +} // namespace client_update_protocol + +#endif // COMPONENTS_CLIENT_UPDATE_PROTOCOL_ECDSA_H_
diff --git a/src/updater_components/client_update_protocol/ecdsa_unittest.cc b/src/updater_components/client_update_protocol/ecdsa_unittest.cc new file mode 100644 index 0000000..71c7441 --- /dev/null +++ b/src/updater_components/client_update_protocol/ecdsa_unittest.cc
@@ -0,0 +1,294 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/client_update_protocol/ecdsa.h" + +#include <stdint.h> + +#include <limits> +#include <memory> +#include <vector> + +#include "base/base64.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "crypto/random.h" +#include "crypto/secure_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace client_update_protocol { + +namespace { + +std::string GetPublicKeyForTesting() { + // How to generate this key: + // openssl ecparam -genkey -name prime256v1 -out ecpriv.pem + // openssl ec -in ecpriv.pem -pubout -out ecpub.pem + + static const char kCupEcdsaTestKey_Base64[] = + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJNOjKyN6UHyUGkGow+xCmQthQXUo" + "9sd7RIXSpVIM768UlbGb/5JrnISjSYejCc/pxQooI6mJTzWL3pZb5TA1DA=="; + + std::string result; + if (!base::Base64Decode(std::string(kCupEcdsaTestKey_Base64), &result)) + return std::string(); + + return result; +} + +} // end namespace + +class CupEcdsaTest : public testing::Test { + protected: + void SetUp() override { + cup_ = Ecdsa::Create(8, GetPublicKeyForTesting()); + ASSERT_TRUE(cup_.get()); + } + + Ecdsa& CUP() { return *cup_; } + + private: + std::unique_ptr<Ecdsa> cup_; +}; + +TEST_F(CupEcdsaTest, SignRequest) { + static const char kRequest[] = "TestSequenceForCupEcdsaUnitTest"; + static const char kRequestHash[] = + "&cup2hreq=" + "cde1f7dc1311ed96813057ca321c2f5a17ea2c9c776ee0eb31965f7985a3074a"; + static const char kKeyId[] = "cup2key=8:"; + + std::string query; + CUP().SignRequest(kRequest, &query); + std::string query2; + CUP().SignRequest(kRequest, &query2); + + EXPECT_FALSE(query.empty()); + EXPECT_FALSE(query2.empty()); + EXPECT_EQ(0UL, query.find(kKeyId)); + EXPECT_EQ(0UL, query2.find(kKeyId)); + EXPECT_NE(std::string::npos, query.find(kRequestHash)); + EXPECT_NE(std::string::npos, query2.find(kRequestHash)); + + // In theory, this is a flaky test, as there's nothing preventing the RNG + // from returning the same nonce twice in a row. In practice, this should + // be fine. + EXPECT_NE(query, query2); +} + +TEST_F(CupEcdsaTest, ValidateResponse_TestETagParsing) { + // Invalid ETags must be gracefully rejected without a crash. + std::string query_discard; + CUP().SignRequest("Request_A", &query_discard); + CUP().OverrideNonceForTesting(8, 12345); + + // Expect a pass for a well-formed etag. + EXPECT_TRUE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5")); + + // Reject empty etags. + EXPECT_FALSE(CUP().ValidateResponse("Response_A", "")); + + // Reject etags with zero-length hashes or signatures, even if the other + // component is wellformed. + EXPECT_FALSE(CUP().ValidateResponse("Response_A", ":")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e" + ":")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5")); + + // Reject etags with non-hex content in either component. + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fb15d24e66c168ac150458__ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e" + ":2727bc2b3c33feb6800a830f4055901d__7d65a84184c5fbeb3f816db0a243f5")); + + // Reject etags where either/both component has a length that's not a + // multiple of 2 (i.e. not a valid hex encoding). + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f")); + + // Reject etags where the hash is even, but not 256 bits. + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5ff")); + + // Reject etags where the signature field is too small to be valid. (Note that + // the case isn't even a signature -- it's a validly encoded ASN.1 NULL.) + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "0500" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243")); + + // Reject etags where the signature field is too big to be a valid signature. + // (This is a validly formed structure, but both ints are over 256 bits.) + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3048" + "202207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "202207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5ff")); + + // Reject etags where the signature is valid DER-encoded ASN.1, but is not + // an ECDSA signature. (This is actually stressing crypto's SignatureValidator + // library, and not CUP's use of it, but it's worth testing here.) Cases: + // * Something that's not a sequence + // * Sequences that contain things other than ints (i.e. octet strings) + // * Sequences that contain a negative int. + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "0406020100020100" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "06200123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + "06200123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3046" + "02047fffffff" + "0220ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243")); + + // Reject etags where the signature is not a valid DER encoding. (Again, this + // is stressing SignatureValidator.) Test cases are: + // * No length field + // * Zero length field + // * One of the ints has truncated content + // * One of the ints has content longer than its length field + // * A positive int is improperly zero-padded + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "30" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3000" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "02207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00" + "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243")); + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3044" + "022000007f24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656" + "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5")); +} + +TEST_F(CupEcdsaTest, ValidateResponse_TestSigning) { + std::string query_discard; + CUP().SignRequest("Request_A", &query_discard); + CUP().OverrideNonceForTesting(8, 12345); + + // How to generate an ECDSA signature: + // echo -n Request_A | sha256sum | cut -d " " -f 1 > h + // echo -n Response_A | sha256sum | cut -d " " -f 1 >> h + // cat h | xxd -r -p > hbin + // echo -n 8:12345 >> hbin + // sha256sum hbin | cut -d " " -f 1 | xxd -r -p > hbin2 + // openssl dgst -hex -sha256 -sign ecpriv.pem hbin2 | cut -d " " -f 2 > sig + // echo -n :Request_A | sha256sum | cut -d " " -f 1 >> sig + // cat sig + // It's useful to throw this in a bash script and parameterize it if you're + // updating this unit test. + + // Valid case: + // * Send "Request_A" with key 8 / nonce 12345 to server. + // * Receive "Response_A", signature, and observed request hash from server. + // * Signature signs HASH(Request_A) | HASH(Response_A) | 8:12345. + // * Observed hash matches HASH(Request_A). + EXPECT_TRUE(CUP().ValidateResponse( + "Response_A", + "3045022077a2d004f1643a92af5d356877c3434c46519ce32882d6e30ef6d154ee9775e3" + "022100aca63c77d34152bdc0918ae0629e82b59314e5459f607cdc5ac95f1a4b7c31a2" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5")); + + // Failure case: "Request_A" made it to the server intact, but the response + // body is modified to "Response_B" on return. The signature is now invalid. + EXPECT_FALSE(CUP().ValidateResponse( + "Response_B", + "3045022077a2d004f1643a92af5d356877c3434c46519ce32882d6e30ef6d154ee9775e3" + "022100aca63c77d34152bdc0918ae0629e82b59314e5459f607cdc5ac95f1a4b7c31a2" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5")); + + // Failure case: Request body was modified to "Request_B" before it reached + // the server. Test a fast reject based on the observed_hash parameter. + EXPECT_FALSE(CUP().ValidateResponse( + "Response_B", + "304402206289a7765f0371c7c48796779747f1166707d5937a99af518845f44af95876" + "8c0220139fe935fde3e6b416ee742f91c6a480113762d78d889a2661de37576866d21c" + ":80e3ef1b373efe5f2a8383a0cf9c89fb2e0cbb8e85db4813655ff5dc05009e7e")); + + // Failure case: Request body was modified to "Request_B" before it reached + // the server. Test a slow reject based on a signature mismatch. + EXPECT_FALSE(CUP().ValidateResponse( + "Response_B", + "304402206289a7765f0371c7c48796779747f1166707d5937a99af518845f44af95876" + "8c0220139fe935fde3e6b416ee742f91c6a480113762d78d889a2661de37576866d21c" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5")); + + // Failure case: Request/response are intact, but the signature is invalid + // because it was signed against a different nonce (67890). + EXPECT_FALSE(CUP().ValidateResponse( + "Response_A", + "3046022100d3bbb1fb4451c8e04a07fe95404cc39121ed0e0bc084f87de19d52eee50a97" + "bf022100dd7d41d467be2af98d9116b0c7ba09740d54578c02a02f74da5f089834be3403" + ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5")); +} + +} // namespace client_update_protocol
diff --git a/src/updater_components/courgette.h b/src/updater_components/courgette.h new file mode 100644 index 0000000..5337566 --- /dev/null +++ b/src/updater_components/courgette.h
@@ -0,0 +1,116 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COURGETTE_COURGETTE_H_ +#define COURGETTE_COURGETTE_H_ + +#include <stddef.h> // Required to define size_t on GCC + +#include "base/files/file.h" +#include "base/files/file_path.h" + +namespace courgette { + +// Status codes for Courgette APIs. +// +// Client code should only rely on the distintion between C_OK and the other +// status codes. +// +enum Status { + C_OK = 1, // Successful operation. + + C_GENERAL_ERROR = 2, // Error other than listed below. + + C_READ_OPEN_ERROR = 3, // Could not open input file for reading. + C_READ_ERROR = 4, // Could not read from opened input file. + + C_WRITE_OPEN_ERROR = 3, // Could not open output file for writing. + C_WRITE_ERROR = 4, // Could not write to opened output file. + + C_BAD_ENSEMBLE_MAGIC = 5, // Ensemble patch has bad magic. + C_BAD_ENSEMBLE_VERSION = 6, // Ensemble patch has wrong version. + C_BAD_ENSEMBLE_HEADER = 7, // Ensemble patch has corrupt header. + C_BAD_ENSEMBLE_CRC = 8, // Ensemble patch has corrupt data. + + C_BAD_TRANSFORM = 12, // Transform mis-specified. + C_BAD_BASE = 13, // Base for transform malformed. + + C_BINARY_DIFF_CRC_ERROR = 14, // Internal diff input doesn't have expected + // CRC. + + // Internal errors. + C_STREAM_ERROR = 20, // Unexpected error from streams.h. + C_STREAM_NOT_CONSUMED = 21, // Stream has extra data, is expected to be + // used up. + C_SERIALIZATION_FAILED = 22, // + C_DESERIALIZATION_FAILED = 23, // + C_INPUT_NOT_RECOGNIZED = 24, // Unrecognized input (not an executable). + C_DISASSEMBLY_FAILED = 25, // + C_ASSEMBLY_FAILED = 26, // + C_ADJUSTMENT_FAILED = 27, // +}; + +// What type of executable is something +// This is part of the patch format. Never reuse an id number. +enum ExecutableType { + EXE_UNKNOWN = 0, + EXE_WIN_32_X86 = 1, + EXE_ELF_32_X86 = 2, + EXE_ELF_32_ARM = 3, + EXE_WIN_32_X64 = 4, +}; + +class SinkStream; +class SinkStreamSet; +class SourceStream; + +class AssemblyProgram; +class EncodedProgram; + +// Applies the patch to the bytes in |old| and writes the transformed ensemble +// to |output|. +// Returns C_OK unless something went wrong. +Status ApplyEnsemblePatch(SourceStream* old, SourceStream* patch, + SinkStream* output); + +// Applies the patch in |patch_file| to the bytes in |old_file| and writes the +// transformed ensemble to |new_file|. +// Returns C_OK unless something went wrong. +// This function first validates that the patch file has a proper header, so the +// function can be used to 'try' a patch. +Status ApplyEnsemblePatch(base::File old_file, + base::File patch_file, + base::File new_file); + +// Applies the patch in |patch_file_name| to the bytes in |old_file_name| and +// writes the transformed ensemble to |new_file_name|. +// Returns C_OK unless something went wrong. +// This function first validates that the patch file has a proper header, so the +// function can be used to 'try' a patch. +Status ApplyEnsemblePatch(const base::FilePath::CharType* old_file_name, + const base::FilePath::CharType* patch_file_name, + const base::FilePath::CharType* new_file_name); + +// Generates a patch that will transform the bytes in |old| into the bytes in +// |target|. +// Returns C_OK unless something when wrong (unexpected). +Status GenerateEnsemblePatch(SourceStream* old, SourceStream* target, + SinkStream* patch); + +// Serializes |encoded| into the stream set. +// Returns C_OK if succeeded, otherwise returns an error status. +Status WriteEncodedProgram(EncodedProgram* encoded, SinkStreamSet* sink); + +// Assembles |encoded|, emitting the bytes into |buffer|. +// Returns C_OK if succeeded, otherwise returns an error status and leaves +// |buffer| in an undefined state. +Status Assemble(EncodedProgram* encoded, SinkStream* buffer); + +// Adjusts |program| to look more like |model|. +// +Status Adjust(const AssemblyProgram& model, AssemblyProgram *program); + +} // namespace courgette + +#endif // COURGETTE_COURGETTE_H_
diff --git a/src/updater_components/crx_file/BUILD.gn b/src/updater_components/crx_file/BUILD.gn new file mode 100644 index 0000000..4e6eacb --- /dev/null +++ b/src/updater_components/crx_file/BUILD.gn
@@ -0,0 +1,82 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/protobuf/proto_library.gni") + +static_library("crx_file") { + sources = [ + "crx_file.h", + "crx_verifier.cc", + "crx_verifier.h", + "id_util.cc", + "id_util.h", + ] + + deps = [ + "//base", + "//crypto", + ] + + public_deps = [ + ":crx3_proto", + ] +} + +static_library("crx_creator") { + sources = [ + "crx_creator.cc", + "crx_creator.h", + ] + + deps = [ + ":crx_file", + "//base", + "//crypto", + ] + + public_deps = [ + ":crx3_proto", + ] +} + +bundle_data("unit_tests_bundle_data") { + visibility = [ ":unit_tests" ] + testonly = true + sources = [ + "//components/test/data/crx_file/sample.zip", + "//components/test/data/crx_file/unsigned.crx3", + "//components/test/data/crx_file/valid.crx2", + "//components/test/data/crx_file/valid_no_publisher.crx3", + "//components/test/data/crx_file/valid_publisher.crx3", + "//components/test/data/crx_file/valid_test_publisher.crx3", + ] + outputs = [ + "{{bundle_resources_dir}}/" + + "{{source_root_relative_dir}}/{{source_file_part}}", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "crx_creator_unittest.cc", + "crx_verifier_unittest.cc", + "id_util_unittest.cc", + ] + + deps = [ + ":crx_creator", + ":crx_file", + ":unit_tests_bundle_data", + "//base", + "//crypto", + "//testing/gtest", + ] +} + +proto_library("crx3_proto") { + sources = [ + "crx3.proto", + ] +}
diff --git a/src/updater_components/crx_file/crx3.proto b/src/updater_components/crx_file/crx3.proto new file mode 100644 index 0000000..2ea91f1 --- /dev/null +++ b/src/updater_components/crx_file/crx3.proto
@@ -0,0 +1,55 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package crx_file; + +// A CRX₃ file is a binary file of the following format: +// [4 octets]: "Cr24", a magic number. +// [4 octets]: The version of the *.crx file format used (currently 3). +// [4 octets]: N, little-endian, the length of the header section. +// [N octets]: The header (the binary encoding of a CrxFileHeader). +// [M octets]: The ZIP archive. +// Clients should reject CRX₃ files that contain an N that is too large for the +// client to safely handle in memory. + +message CrxFileHeader { + // PSS signature with RSA public key. The public key is formatted as a + // X.509 SubjectPublicKeyInfo block, as in CRX₂. In the common case of a + // developer key proof, the first 128 bits of the SHA-256 hash of the + // public key must equal the crx_id. + repeated AsymmetricKeyProof sha256_with_rsa = 2; + + // ECDSA signature, using the NIST P-256 curve. Public key appears in + // named-curve format. + // The pinned algorithm will be this, at least on 2017-01-01. + repeated AsymmetricKeyProof sha256_with_ecdsa = 3; + + // The binary form of a SignedData message. We do not use a nested + // SignedData message, as handlers of this message must verify the proofs + // on exactly these bytes, so it is convenient to parse in two steps. + // + // All proofs in this CrxFile message are on the value + // "CRX3 SignedData\x00" + signed_header_size + signed_header_data + + // archive, where "\x00" indicates an octet with value 0, "CRX3 SignedData" + // is encoded using UTF-8, signed_header_size is the size in octets of the + // contents of this field and is encoded using 4 octets in little-endian + // order, signed_header_data is exactly the content of this field, and + // archive is the remaining contents of the file following the header. + optional bytes signed_header_data = 10000; +} + +message AsymmetricKeyProof { + optional bytes public_key = 1; + optional bytes signature = 2; +} + +message SignedData { + // This is simple binary, not UTF-8 encoded mpdecimal; i.e. it is exactly + // 16 bytes long. + optional bytes crx_id = 1; +}
diff --git a/src/updater_components/crx_file/crx_creator.cc b/src/updater_components/crx_file/crx_creator.cc new file mode 100644 index 0000000..8ec6bb3 --- /dev/null +++ b/src/updater_components/crx_file/crx_creator.cc
@@ -0,0 +1,129 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crx_file/crx_creator.h" + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/stl_util.h" +#include "components/crx_file/crx3.pb.h" +#include "components/crx_file/crx_file.h" +#include "crypto/rsa_private_key.h" +#include "crypto/sha2.h" +#include "crypto/signature_creator.h" + +namespace crx_file { + +namespace { + +std::string GetCrxId(const std::string& key) { + uint8_t hash[16] = {}; // CRX IDs are 16 bytes long. + crypto::SHA256HashString(key, hash, sizeof(hash)); + static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size."); + return std::string(reinterpret_cast<char*>(hash), sizeof(hash)); +} + +// Read to the end of the file, updating the signer. +CreatorResult ReadAndSignArchive(base::File* file, + crypto::SignatureCreator* signer, + std::vector<uint8_t>* signature) { + uint8_t buffer[1 << 12] = {}; + int read = 0; + static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size."); + while ((read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), + base::size(buffer))) > 0) { + if (!signer->Update(buffer, read)) + return CreatorResult::ERROR_SIGNING_FAILURE; + } + if (read < 0) + return CreatorResult::ERROR_SIGNING_FAILURE; + return signer->Final(signature) ? CreatorResult::OK + : CreatorResult::ERROR_SIGNING_FAILURE; +} + +bool WriteBuffer(base::File* file, const char buffer[], int len) { + return file->WriteAtCurrentPos(buffer, len) == len; +} + +bool WriteArchive(base::File* out, base::File* in) { + char buffer[1 << 12] = {}; + int read = 0; + in->Seek(base::File::Whence::FROM_BEGIN, 0); + while ((read = in->ReadAtCurrentPos(buffer, base::size(buffer))) > 0) { + if (out->WriteAtCurrentPos(buffer, read) != read) + return false; + } + return read == 0; +} + +} // namespace + +CreatorResult Create(const base::FilePath& output_path, + const base::FilePath& zip_path, + crypto::RSAPrivateKey* signing_key) { + // Get the public key. + std::vector<uint8_t> public_key; + signing_key->ExportPublicKey(&public_key); + const std::string public_key_str(public_key.begin(), public_key.end()); + + // Assemble SignedData section. + SignedData signed_header_data; + signed_header_data.set_crx_id(GetCrxId(public_key_str)); + const std::string signed_header_data_str = + signed_header_data.SerializeAsString(); + const int signed_header_size = signed_header_data_str.size(); + const uint8_t signed_header_size_octets[] = { + signed_header_size, signed_header_size >> 8, signed_header_size >> 16, + signed_header_size >> 24}; + + // Create a signer, init with purpose, SignedData length, run SignedData + // through, run ZIP through. + auto signer = crypto::SignatureCreator::Create( + signing_key, crypto::SignatureCreator::HashAlgorithm::SHA256); + signer->Update(kSignatureContext, base::size(kSignatureContext)); + signer->Update(signed_header_size_octets, + base::size(signed_header_size_octets)); + signer->Update( + reinterpret_cast<const uint8_t*>(signed_header_data_str.data()), + signed_header_data_str.size()); + base::File file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ); + if (!file.IsValid()) + return CreatorResult::ERROR_FILE_NOT_READABLE; + std::vector<uint8_t> signature; + const CreatorResult signing_result = + ReadAndSignArchive(&file, signer.get(), &signature); + if (signing_result != CreatorResult::OK) + return signing_result; + + // Create CRXFileHeader. + CrxFileHeader header; + AsymmetricKeyProof* proof = header.add_sha256_with_rsa(); + proof->set_public_key(public_key_str); + proof->set_signature(std::string(signature.begin(), signature.end())); + header.set_signed_header_data(signed_header_data_str); + const std::string header_str = header.SerializeAsString(); + const int header_size = header_str.size(); + const uint8_t header_size_octets[] = {header_size, header_size >> 8, + header_size >> 16, header_size >> 24}; + + // Write CRX. + const uint8_t format_version_octets[] = {3, 0, 0, 0}; + base::File crx(output_path, + base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + if (!crx.IsValid()) + return CreatorResult::ERROR_FILE_NOT_WRITABLE; + static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size."); + if (!WriteBuffer(&crx, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize) || + !WriteBuffer(&crx, reinterpret_cast<const char*>(format_version_octets), + base::size(format_version_octets)) || + !WriteBuffer(&crx, reinterpret_cast<const char*>(header_size_octets), + base::size(header_size_octets)) || + !WriteBuffer(&crx, header_str.c_str(), header_str.length()) || + !WriteArchive(&crx, &file)) { + return CreatorResult::ERROR_FILE_WRITE_FAILURE; + } + return CreatorResult::OK; +} + +} // namespace crx_file
diff --git a/src/updater_components/crx_file/crx_creator.h b/src/updater_components/crx_file/crx_creator.h new file mode 100644 index 0000000..a8c2094 --- /dev/null +++ b/src/updater_components/crx_file/crx_creator.h
@@ -0,0 +1,35 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRX_FILE_CRX_CREATOR_H_ +#define COMPONENTS_CRX_FILE_CRX_CREATOR_H_ + +namespace base { +class FilePath; +} // namespace base + +namespace crypto { +class RSAPrivateKey; +} // namespace crypto + +namespace crx_file { + +enum class CreatorResult { + OK, // The CRX file was successfully created. + ERROR_SIGNING_FAILURE, + ERROR_FILE_NOT_READABLE, + ERROR_FILE_NOT_WRITABLE, + ERROR_FILE_WRITE_FAILURE, +}; + +// Create a CRX3 file at |output_path|, using the contents of the ZIP archive +// located at |zip_path| and signing with (and deriving the CRX ID from) +// |signing_key|. +CreatorResult Create(const base::FilePath& output_path, + const base::FilePath& zip_path, + crypto::RSAPrivateKey* signing_key); + +} // namespace crx_file + +#endif // COMPONENTS_CRX_FILE_CRX_CREATOR_H_
diff --git a/src/updater_components/crx_file/crx_creator_unittest.cc b/src/updater_components/crx_file/crx_creator_unittest.cc new file mode 100644 index 0000000..086a5a5 --- /dev/null +++ b/src/updater_components/crx_file/crx_creator_unittest.cc
@@ -0,0 +1,61 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crx_file/crx_creator.h" +#include "base/base64.h" +#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "components/crx_file/crx_verifier.h" +#include "crypto/rsa_private_key.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +base::FilePath TestFile(const std::string& file) { + base::FilePath path; + base::PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("components") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("crx_file") + .AppendASCII(file); +} + +} // namespace + +namespace crx_file { + +using CrxCreatorTest = testing::Test; + +TEST_F(CrxCreatorTest, Create) { + // Set up a signing key. + auto signing_key = crypto::RSAPrivateKey::Create(4096); + std::vector<uint8_t> public_key; + signing_key->ExportPublicKey(&public_key); + std::string expected_public_key; + base::Base64Encode(std::string(public_key.begin(), public_key.end()), + &expected_public_key); + + // Create a CRX File. + base::FilePath tempFile; + EXPECT_TRUE(base::CreateTemporaryFile(&tempFile)); + EXPECT_EQ(CreatorResult::OK, + Create(tempFile, TestFile("sample.zip"), signing_key.get())); + + // Test that the created file can be verified. + const std::vector<std::vector<uint8_t>> keys; + const std::vector<uint8_t> hash; + std::string public_key_in_crx; + EXPECT_EQ(VerifierResult::OK_FULL, + Verify(tempFile, VerifierFormat::CRX3, keys, hash, + &public_key_in_crx, nullptr)); + EXPECT_EQ(expected_public_key, public_key_in_crx); + + // Delete the file. + EXPECT_TRUE(base::DeleteFile(tempFile, false)); +} + +} // namespace crx_file
diff --git a/src/updater_components/crx_file/crx_file.h b/src/updater_components/crx_file/crx_file.h new file mode 100644 index 0000000..4e258e1 --- /dev/null +++ b/src/updater_components/crx_file/crx_file.h
@@ -0,0 +1,18 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRX_FILE_CRX_FILE_H_ +#define COMPONENTS_CRX_FILE_CRX_FILE_H_ + +namespace crx_file { + +// The magic string embedded in the header. +constexpr char kCrxFileHeaderMagic[] = "Cr24"; +constexpr char kCrxDiffFileHeaderMagic[] = "CrOD"; +constexpr int kCrxFileHeaderMagicSize = 4; +constexpr unsigned char kSignatureContext[] = u8"CRX3 SignedData"; + +} // namespace crx_file + +#endif // COMPONENTS_CRX_FILE_CRX_FILE_H_
diff --git a/src/updater_components/crx_file/crx_verifier.cc b/src/updater_components/crx_file/crx_verifier.cc new file mode 100644 index 0000000..0d20251 --- /dev/null +++ b/src/updater_components/crx_file/crx_verifier.cc
@@ -0,0 +1,331 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crx_file/crx_verifier.h" + +#include <cstring> +#include <iterator> +#include <memory> +#include <set> +#include <utility> + +#include "base/base64.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "components/crx_file/crx3.pb.h" +#include "components/crx_file/crx_file.h" +#include "components/crx_file/id_util.h" +#include "crypto/secure_hash.h" +#include "crypto/secure_util.h" +#include "crypto/sha2.h" +#include "crypto/signature_verifier.h" + +namespace crx_file { + +namespace { + +// The maximum size the Crx2 parser will tolerate for a public key. +constexpr uint32_t kMaxPublicKeySize = 1 << 16; + +// The maximum size the Crx2 parser will tolerate for a signature. +constexpr uint32_t kMaxSignatureSize = 1 << 16; + +// The maximum size the Crx3 parser will tolerate for a header. +constexpr uint32_t kMaxHeaderSize = 1 << 18; + +// The SHA256 hash of the DER SPKI "ecdsa_2017_public" Crx3 key. +constexpr uint8_t kPublisherKeyHash[] = { + 0x61, 0xf7, 0xf2, 0xa6, 0xbf, 0xcf, 0x74, 0xcd, 0x0b, 0xc1, 0xfe, + 0x24, 0x97, 0xcc, 0x9b, 0x04, 0x25, 0x4c, 0x65, 0x8f, 0x79, 0xf2, + 0x14, 0x53, 0x92, 0x86, 0x7e, 0xa8, 0x36, 0x63, 0x67, 0xcf}; + +// The SHA256 hash of the DER SPKI "ecdsa_2017_public" Crx3 test key. +constexpr uint8_t kPublisherTestKeyHash[] = { + 0x6c, 0x46, 0x41, 0x3b, 0x00, 0xd0, 0xfa, 0x0e, 0x72, 0xc8, 0xd2, + 0x5f, 0x64, 0xf3, 0xa6, 0x17, 0x03, 0x0d, 0xde, 0x21, 0x61, 0xbe, + 0xb7, 0x95, 0x91, 0x95, 0x83, 0x68, 0x12, 0xe9, 0x78, 0x1e}; + +using VerifierCollection = + std::vector<std::unique_ptr<crypto::SignatureVerifier>>; +using RepeatedProof = google::protobuf::RepeatedPtrField<AsymmetricKeyProof>; + +int ReadAndHashBuffer(uint8_t* buffer, + int length, + base::File* file, + crypto::SecureHash* hash) { + static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size."); + int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length); + if (read > 0) + hash->Update(buffer, read); + return read; +} + +// Returns UINT32_MAX in the case of an unexpected EOF or read error, else +// returns the read uint32. +uint32_t ReadAndHashLittleEndianUInt32(base::File* file, + crypto::SecureHash* hash) { + uint8_t buffer[4] = {}; + if (ReadAndHashBuffer(buffer, 4, file, hash) != 4) + return UINT32_MAX; + return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]; +} + +// Read to the end of the file, updating the hash and all verifiers. +bool ReadHashAndVerifyArchive(base::File* file, + crypto::SecureHash* hash, + const VerifierCollection& verifiers) { + uint8_t buffer[1 << 12] = {}; + size_t len = 0; + while ((len = ReadAndHashBuffer(buffer, base::size(buffer), file, hash)) > + 0) { + for (auto& verifier : verifiers) + verifier->VerifyUpdate(base::make_span(buffer, len)); + } + for (auto& verifier : verifiers) { + if (!verifier->VerifyFinal()) + return false; + } + return len == 0; +} + +// The remaining contents of a Crx3 file are [header-size][header][archive]. +// [header] is an encoded protocol buffer and contains both a signed and +// unsigned section. The unsigned section contains a set of key/signature pairs, +// and the signed section is the encoding of another protocol buffer. All +// signatures cover [prefix][signed-header-size][signed-header][archive]. +VerifierResult VerifyCrx3( + base::File* file, + crypto::SecureHash* hash, + const std::vector<std::vector<uint8_t>>& required_key_hashes, + std::string* public_key, + std::string* crx_id, + bool require_publisher_key, + bool accept_publisher_test_key) { + // Parse [header-size] and [header]. + const uint32_t header_size = ReadAndHashLittleEndianUInt32(file, hash); + if (header_size > kMaxHeaderSize) + return VerifierResult::ERROR_HEADER_INVALID; + std::vector<uint8_t> header_bytes(header_size); + // Assuming kMaxHeaderSize can fit in an int, the following cast is safe. + if (ReadAndHashBuffer(header_bytes.data(), header_size, file, hash) != + static_cast<int>(header_size)) + return VerifierResult::ERROR_HEADER_INVALID; + CrxFileHeader header; + if (!header.ParseFromArray(header_bytes.data(), header_size)) + return VerifierResult::ERROR_HEADER_INVALID; + + // Parse [signed-header]. + const std::string& signed_header_data_str = header.signed_header_data(); + SignedData signed_header_data; + if (!signed_header_data.ParseFromString(signed_header_data_str)) + return VerifierResult::ERROR_HEADER_INVALID; + const std::string& crx_id_encoded = signed_header_data.crx_id(); + const std::string declared_crx_id = id_util::GenerateIdFromHex( + base::HexEncode(crx_id_encoded.data(), crx_id_encoded.size())); + + // Create a little-endian representation of [signed-header-size]. + const int signed_header_size = signed_header_data_str.size(); + const uint8_t header_size_octets[] = { + signed_header_size, signed_header_size >> 8, signed_header_size >> 16, + signed_header_size >> 24}; + + // Create a set of all required key hashes. + std::set<std::vector<uint8_t>> required_key_set(required_key_hashes.begin(), + required_key_hashes.end()); + + using ProofFetcher = const RepeatedProof& (CrxFileHeader::*)() const; + ProofFetcher rsa = &CrxFileHeader::sha256_with_rsa; + ProofFetcher ecdsa = &CrxFileHeader::sha256_with_ecdsa; + + std::string public_key_bytes; + VerifierCollection verifiers; + verifiers.reserve(header.sha256_with_rsa_size() + + header.sha256_with_ecdsa_size()); + const std::vector< + std::pair<ProofFetcher, crypto::SignatureVerifier::SignatureAlgorithm>> + proof_types = { + std::make_pair(rsa, crypto::SignatureVerifier::RSA_PKCS1_SHA256), + std::make_pair(ecdsa, crypto::SignatureVerifier::ECDSA_SHA256)}; + + std::vector<uint8_t> publisher_key(std::begin(kPublisherKeyHash), + std::end(kPublisherKeyHash)); + base::Optional<std::vector<uint8_t>> publisher_test_key; + if (accept_publisher_test_key) { + publisher_test_key.emplace(std::begin(kPublisherTestKeyHash), + std::end(kPublisherTestKeyHash)); + } + bool found_publisher_key = false; + + // Initialize all verifiers and update them with + // [prefix][signed-header-size][signed-header]. + // Clear any elements of required_key_set that are encountered, and watch for + // the developer key. + for (const auto& proof_type : proof_types) { + for (const auto& proof : (header.*proof_type.first)()) { + const std::string& key = proof.public_key(); + const std::string& sig = proof.signature(); + if (id_util::GenerateId(key) == declared_crx_id) + public_key_bytes = key; + std::vector<uint8_t> key_hash(crypto::kSHA256Length); + crypto::SHA256HashString(key, key_hash.data(), key_hash.size()); + required_key_set.erase(key_hash); + DCHECK_EQ(accept_publisher_test_key, publisher_test_key.has_value()); + found_publisher_key = + found_publisher_key || key_hash == publisher_key || + (accept_publisher_test_key && key_hash == *publisher_test_key); + auto v = std::make_unique<crypto::SignatureVerifier>(); + static_assert(sizeof(unsigned char) == sizeof(uint8_t), + "Unsupported char size."); + if (!v->VerifyInit(proof_type.second, + base::as_bytes(base::make_span(sig)), + base::as_bytes(base::make_span(key)))) + return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED; + v->VerifyUpdate(kSignatureContext); + v->VerifyUpdate(header_size_octets); + v->VerifyUpdate(base::as_bytes(base::make_span(signed_header_data_str))); + verifiers.push_back(std::move(v)); + } + } + if (public_key_bytes.empty() || !required_key_set.empty()) + return VerifierResult::ERROR_REQUIRED_PROOF_MISSING; + + if (require_publisher_key && !found_publisher_key) + return VerifierResult::ERROR_REQUIRED_PROOF_MISSING; + + // Update and finalize the verifiers with [archive]. + if (!ReadHashAndVerifyArchive(file, hash, verifiers)) + return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED; + + base::Base64Encode(public_key_bytes, public_key); + *crx_id = declared_crx_id; + return VerifierResult::OK_FULL; +} + +VerifierResult VerifyCrx2( + base::File* file, + crypto::SecureHash* hash, + const std::vector<std::vector<uint8_t>>& required_key_hashes, + std::string* public_key, + std::string* crx_id) { + const uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash); + if (key_size > kMaxPublicKeySize) + return VerifierResult::ERROR_HEADER_INVALID; + const uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash); + if (sig_size > kMaxSignatureSize) + return VerifierResult::ERROR_HEADER_INVALID; + std::vector<uint8_t> key(key_size); + if (ReadAndHashBuffer(key.data(), key_size, file, hash) != + static_cast<int>(key_size)) + return VerifierResult::ERROR_HEADER_INVALID; + for (const auto& expected_hash : required_key_hashes) { + // In practice we expect zero or one key_hashes_ for Crx2 files. + std::vector<uint8_t> hash(crypto::kSHA256Length); + std::unique_ptr<crypto::SecureHash> sha256 = + crypto::SecureHash::Create(crypto::SecureHash::SHA256); + sha256->Update(key.data(), key.size()); + sha256->Finish(hash.data(), hash.size()); + if (hash != expected_hash) + return VerifierResult::ERROR_REQUIRED_PROOF_MISSING; + } + + std::vector<uint8_t> sig(sig_size); + if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) != + static_cast<int>(sig_size)) + return VerifierResult::ERROR_HEADER_INVALID; + std::vector<std::unique_ptr<crypto::SignatureVerifier>> verifiers; + verifiers.push_back(std::make_unique<crypto::SignatureVerifier>()); + if (!verifiers[0]->VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1, sig, + key)) { + return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED; + } + + if (!ReadHashAndVerifyArchive(file, hash, verifiers)) + return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED; + + const std::string public_key_bytes(key.begin(), key.end()); + base::Base64Encode(public_key_bytes, public_key); + *crx_id = id_util::GenerateId(public_key_bytes); + return VerifierResult::OK_FULL; +} + +} // namespace + +VerifierResult Verify( + const base::FilePath& crx_path, + const VerifierFormat& format, + const std::vector<std::vector<uint8_t>>& required_key_hashes, + const std::vector<uint8_t>& required_file_hash, + std::string* public_key, + std::string* crx_id) { + std::string public_key_local; + std::string crx_id_local; + base::File file(crx_path, base::File::FLAG_OPEN | base::File::FLAG_READ); + if (!file.IsValid()) + return VerifierResult::ERROR_FILE_NOT_READABLE; + + std::unique_ptr<crypto::SecureHash> file_hash = + crypto::SecureHash::Create(crypto::SecureHash::SHA256); + + // Magic number. + bool diff = false; + char buffer[kCrxFileHeaderMagicSize] = {}; + if (file.ReadAtCurrentPos(buffer, kCrxFileHeaderMagicSize) != + kCrxFileHeaderMagicSize) + return VerifierResult::ERROR_HEADER_INVALID; + if (!strncmp(buffer, kCrxDiffFileHeaderMagic, kCrxFileHeaderMagicSize)) + diff = true; + else if (strncmp(buffer, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize)) + return VerifierResult::ERROR_HEADER_INVALID; + file_hash->Update(buffer, sizeof(buffer)); + + // Version number. + const uint32_t version = + ReadAndHashLittleEndianUInt32(&file, file_hash.get()); + VerifierResult result; + if (version == 2) + LOG(WARNING) << "File '" << crx_path + << "' is in CRX2 format, which is deprecated and will not be " + "supported in M78+"; + if (format == VerifierFormat::CRX2_OR_CRX3 && + (version == 2 || (diff && version == 0))) { + result = VerifyCrx2(&file, file_hash.get(), required_key_hashes, + &public_key_local, &crx_id_local); + } else if (version == 3) { + bool require_publisher_key = + format == VerifierFormat::CRX3_WITH_PUBLISHER_PROOF || + format == VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF; + result = + VerifyCrx3(&file, file_hash.get(), required_key_hashes, + &public_key_local, &crx_id_local, require_publisher_key, + format == VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF); + } else { + result = VerifierResult::ERROR_HEADER_INVALID; + } + if (result != VerifierResult::OK_FULL) + return result; + + // Finalize file hash. + uint8_t final_hash[crypto::kSHA256Length] = {}; + file_hash->Finish(final_hash, sizeof(final_hash)); + if (!required_file_hash.empty()) { + if (required_file_hash.size() != crypto::kSHA256Length) + return VerifierResult::ERROR_EXPECTED_HASH_INVALID; + if (!crypto::SecureMemEqual(final_hash, required_file_hash.data(), + crypto::kSHA256Length)) + return VerifierResult::ERROR_FILE_HASH_FAILED; + } + + // All is well. Set the out-params and return. + if (public_key) + *public_key = public_key_local; + if (crx_id) + *crx_id = crx_id_local; + return diff ? VerifierResult::OK_DELTA : VerifierResult::OK_FULL; +} + +} // namespace crx_file
diff --git a/src/updater_components/crx_file/crx_verifier.h b/src/updater_components/crx_file/crx_verifier.h new file mode 100644 index 0000000..b770626 --- /dev/null +++ b/src/updater_components/crx_file/crx_verifier.h
@@ -0,0 +1,56 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRX_FILE_CRX_VERIFIER_H_ +#define COMPONENTS_CRX_FILE_CRX_VERIFIER_H_ + +#include <stdint.h> +#include <string> +#include <vector> + +namespace base { +class FilePath; +} // namespace base + +namespace crx_file { + +enum class VerifierFormat { + CRX2_OR_CRX3, // Accept Crx2 or Crx3. + CRX3, // Accept only Crx3. + CRX3_WITH_TEST_PUBLISHER_PROOF, // Accept only Crx3 with a test or production + // publisher proof. + CRX3_WITH_PUBLISHER_PROOF, // Accept only Crx3 with a production + // publisher proof. +}; + +enum class VerifierResult { + OK_FULL, // The file verifies as a correct full CRX file. + OK_DELTA, // The file verifies as a correct differential CRX file. + ERROR_FILE_NOT_READABLE, // Cannot open the CRX file. + ERROR_HEADER_INVALID, // Failed to parse or understand CRX header. + ERROR_EXPECTED_HASH_INVALID, // Expected hash is not well-formed. + ERROR_FILE_HASH_FAILED, // The file's actual hash != the expected hash. + ERROR_SIGNATURE_INITIALIZATION_FAILED, // A signature or key is malformed. + ERROR_SIGNATURE_VERIFICATION_FAILED, // A signature doesn't match. + ERROR_REQUIRED_PROOF_MISSING, // RequireKeyProof was unsatisfied. +}; + +// Verify the file at |crx_path| as a valid Crx of |format|. The Crx must be +// well-formed, contain no invalid proofs, match the |required_file_hash| (if +// non-empty), and contain a proof with each of the |required_key_hashes|. +// If and only if this function returns OK_FULL or OK_DELTA, and only if +// |public_key| / |crx_id| are non-null, they will be updated to contain the +// public key (PEM format, without the header/footer) and crx id (encoded in +// base16 using the characters [a-p]). +VerifierResult Verify( + const base::FilePath& crx_path, + const VerifierFormat& format, + const std::vector<std::vector<uint8_t>>& required_key_hashes, + const std::vector<uint8_t>& required_file_hash, + std::string* public_key, + std::string* crx_id); + +} // namespace crx_file + +#endif // COMPONENTS_CRX_FILE_CRX_VERIFIER_H_
diff --git a/src/updater_components/crx_file/crx_verifier_unittest.cc b/src/updater_components/crx_file/crx_verifier_unittest.cc new file mode 100644 index 0000000..8028b7c --- /dev/null +++ b/src/updater_components/crx_file/crx_verifier_unittest.cc
@@ -0,0 +1,247 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crx_file/crx_verifier.h" +#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "base/strings/string_number_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +base::FilePath TestFile(const std::string& file) { + base::FilePath path; + base::PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("components") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("crx_file") + .AppendASCII(file); +} + +constexpr char kOjjHash[] = "ojjgnpkioondelmggbekfhllhdaimnho"; +constexpr char kOjjKey[] = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA230uN7vYDEhdDlb4/" + "+pg2pfL8p0FFzCF/O146NB3D5dPKuLbnNphn0OUzOrDzR/Z1XLVDlDyiA6xnb+qeRp7H8n7Wk/" + "/gvVDNArZyForlVqWdaHLhl4dyZoNJwPKsggf30p/" + "MxCbNfy2rzFujzn2nguOrJKzWvNt0BFqssrBpzOQl69blBezE2ZYGOnYW8mPgQV29ekIgOfJk2" + "GgXoJBQQRRsjoPmUY7GDuEKudEB/" + "CmWh3+" + "mCsHBHFWbqtGhSN4YCAw3DYQzwdTcIVaIA8f2Uo4AZ4INKkrEPRL8o9mZDYtO2YHIQg8pMSRMa" + "6AawBNYi9tZScnmgl5L1qE6z5oIwIDAQAB"; + +constexpr char kJlnHash[] = "jlnmailbicnpnbggmhfebbomaddckncf"; +constexpr char kJlnKey[] = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtYd4M8wBjlPsc/wxS1/uXKMD6GtI7/" + "0GLxRe6UXTYtDk91u/" + "FdJRK9jHws+" + "FO6Yi3XcJGtMvuojiB1j4bdiYBfvRgfTD4b7krWtWM2udKPBtHI9ikAT5aom5Bda8rCPNyaqXC" + "6Ax+KTgQpeeJglYu7TTd/" + "AePyvlRHtCKNkcvRQLY0b6hccALqoTzyTueDX12c8Htg76syEPbz7hSIPPfq6KEGvuVSxWAejy" + "/y6EhwAdXRLpegul9KmL94OY1G6dpycUKwyKeXOcB6Qj5iKNcOqJAaSLxoOZby4G3cI1BcQpp/" + "3vYccJ4qouDMfaanLe8CvFlLp4VOn833aJ8PYpLQIDAQAB"; + +} // namespace + +namespace crx_file { + +using CrxVerifierTest = testing::Test; + +TEST_F(CrxVerifierTest, ValidFullCrx2) { + const std::vector<std::vector<uint8_t>> keys; + const std::vector<uint8_t> hash; + std::string public_key; + std::string crx_id; + + EXPECT_EQ(VerifierResult::OK_FULL, + Verify(TestFile("valid.crx2"), VerifierFormat::CRX2_OR_CRX3, keys, + hash, &public_key, &crx_id)); + EXPECT_EQ(std::string(kOjjHash), crx_id); + EXPECT_EQ(std::string(kOjjKey), public_key); +} + +TEST_F(CrxVerifierTest, ValidFullCrx3) { + const std::vector<std::vector<uint8_t>> keys; + const std::vector<uint8_t> hash; + std::string public_key = "UNSET"; + std::string crx_id = "UNSET"; + + EXPECT_EQ(VerifierResult::OK_FULL, Verify(TestFile("valid_no_publisher.crx3"), + VerifierFormat::CRX2_OR_CRX3, keys, + hash, &public_key, &crx_id)); + EXPECT_EQ(std::string(kOjjHash), crx_id); + EXPECT_EQ(std::string(kOjjKey), public_key); + + public_key = "UNSET"; + crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::OK_FULL, + Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3, + keys, hash, &public_key, &crx_id)); + EXPECT_EQ(std::string(kOjjHash), crx_id); + EXPECT_EQ(std::string(kOjjKey), public_key); +} + +TEST_F(CrxVerifierTest, Crx3RejectsCrx2) { + const std::vector<std::vector<uint8_t>> keys; + const std::vector<uint8_t> hash; + std::string public_key = "UNSET"; + std::string crx_id = "UNSET"; + + EXPECT_EQ(VerifierResult::ERROR_HEADER_INVALID, + Verify(TestFile("valid.crx2"), VerifierFormat::CRX3, keys, hash, + &public_key, &crx_id)); + EXPECT_EQ("UNSET", crx_id); + EXPECT_EQ("UNSET", public_key); +} + +TEST_F(CrxVerifierTest, VerifiesFileHash) { + const std::vector<std::vector<uint8_t>> keys; + std::vector<uint8_t> hash; + EXPECT_TRUE(base::HexStringToBytes( + "d033c510f9e4ee081ccb60ea2bf530dc2e5cb0e71085b55503c8b13b74515fe4", + &hash)); + std::string public_key = "UNSET"; + std::string crx_id = "UNSET"; + + EXPECT_EQ(VerifierResult::OK_FULL, Verify(TestFile("valid_no_publisher.crx3"), + VerifierFormat::CRX2_OR_CRX3, keys, + hash, &public_key, &crx_id)); + EXPECT_EQ(std::string(kOjjHash), crx_id); + EXPECT_EQ(std::string(kOjjKey), public_key); + + hash.clear(); + EXPECT_TRUE(base::HexStringToBytes(std::string(32, '0'), &hash)); + public_key = "UNSET"; + crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::ERROR_EXPECTED_HASH_INVALID, + Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3, + keys, hash, &public_key, &crx_id)); + EXPECT_EQ("UNSET", crx_id); + EXPECT_EQ("UNSET", public_key); + + hash.clear(); + EXPECT_TRUE(base::HexStringToBytes(std::string(64, '0'), &hash)); + public_key = "UNSET"; + crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::ERROR_FILE_HASH_FAILED, + Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3, + keys, hash, &public_key, &crx_id)); + EXPECT_EQ("UNSET", crx_id); + EXPECT_EQ("UNSET", public_key); +} + +TEST_F(CrxVerifierTest, ChecksRequiredKeyHashes) { + const std::vector<uint8_t> hash; + + std::vector<uint8_t> good_key; + EXPECT_TRUE(base::HexStringToBytes( + "e996dfa8eed34bc6614a57bb7308cd7e519bcc690841e1969f7cb173ef16800a", + &good_key)); + const std::vector<std::vector<uint8_t>> good_keys = {good_key}; + std::string public_key = "UNSET"; + std::string crx_id = "UNSET"; + EXPECT_EQ( + VerifierResult::OK_FULL, + Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX2_OR_CRX3, + good_keys, hash, &public_key, &crx_id)); + EXPECT_EQ(std::string(kOjjHash), crx_id); + EXPECT_EQ(std::string(kOjjKey), public_key); + + std::vector<uint8_t> bad_key; + EXPECT_TRUE(base::HexStringToBytes(std::string(64, '0'), &bad_key)); + const std::vector<std::vector<uint8_t>> bad_keys = {bad_key}; + public_key = "UNSET"; + crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING, + Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3, + bad_keys, hash, &public_key, &crx_id)); + EXPECT_EQ("UNSET", crx_id); + EXPECT_EQ("UNSET", public_key); +} + +TEST_F(CrxVerifierTest, ChecksPinnedKey) { + const std::vector<uint8_t> hash; + const std::vector<std::vector<uint8_t>> keys; + std::string public_key = "UNSET"; + std::string crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::OK_FULL, + Verify(TestFile("valid_publisher.crx3"), + VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash, + &public_key, &crx_id)); + EXPECT_EQ(std::string(kOjjHash), crx_id); + EXPECT_EQ(std::string(kOjjKey), public_key); + + public_key = "UNSET"; + crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING, + Verify(TestFile("valid_test_publisher.crx3"), + VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash, + &public_key, &crx_id)); + EXPECT_EQ("UNSET", crx_id); + EXPECT_EQ("UNSET", public_key); + + public_key = "UNSET"; + crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING, + Verify(TestFile("valid_no_publisher.crx3"), + VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash, + &public_key, &crx_id)); + EXPECT_EQ("UNSET", crx_id); + EXPECT_EQ("UNSET", public_key); +} + +TEST_F(CrxVerifierTest, ChecksPinnedKeyAcceptsTest) { + const std::vector<uint8_t> hash; + const std::vector<std::vector<uint8_t>> keys; + std::string public_key = "UNSET"; + std::string crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::OK_FULL, + Verify(TestFile("valid_publisher.crx3"), + VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF, keys, hash, + &public_key, &crx_id)); + EXPECT_EQ(std::string(kOjjHash), crx_id); + EXPECT_EQ(std::string(kOjjKey), public_key); + + public_key = "UNSET"; + crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::OK_FULL, + Verify(TestFile("valid_test_publisher.crx3"), + VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF, keys, hash, + &public_key, &crx_id)); + EXPECT_EQ(std::string(kJlnHash), crx_id); + EXPECT_EQ(std::string(kJlnKey), public_key); + + public_key = "UNSET"; + crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING, + Verify(TestFile("valid_no_publisher.crx3"), + VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF, keys, hash, + &public_key, &crx_id)); + EXPECT_EQ("UNSET", crx_id); + EXPECT_EQ("UNSET", public_key); +} + +TEST_F(CrxVerifierTest, NullptrSafe) { + const std::vector<uint8_t> hash; + const std::vector<std::vector<uint8_t>> keys; + EXPECT_EQ(VerifierResult::OK_FULL, + Verify(TestFile("valid_publisher.crx3"), + VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash, + nullptr, nullptr)); +} + +TEST_F(CrxVerifierTest, RequiresDeveloperKey) { + const std::vector<uint8_t> hash; + const std::vector<std::vector<uint8_t>> keys; + std::string public_key = "UNSET"; + std::string crx_id = "UNSET"; + EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING, + Verify(TestFile("unsigned.crx3"), VerifierFormat::CRX2_OR_CRX3, + keys, hash, &public_key, &crx_id)); + EXPECT_EQ("UNSET", crx_id); + EXPECT_EQ("UNSET", public_key); +} + +} // namespace crx_file
diff --git a/src/updater_components/crx_file/id_util.cc b/src/updater_components/crx_file/id_util.cc new file mode 100644 index 0000000..a6250c1 --- /dev/null +++ b/src/updater_components/crx_file/id_util.cc
@@ -0,0 +1,105 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crx_file/id_util.h" + +#include <stdint.h> + +#include "base/files/file_path.h" +#include "base/hash/sha1.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "build/build_config.h" +#include "crypto/sha2.h" + +namespace { + +// Converts a normal hexadecimal string into the alphabet used by extensions. +// We use the characters 'a'-'p' instead of '0'-'f' to avoid ever having a +// completely numeric host, since some software interprets that as an IP +// address. +static void ConvertHexadecimalToIDAlphabet(std::string* id) { + for (auto& ch : *id) { + int val; + if (base::HexStringToInt(base::StringPiece(&ch, 1), &val)) { + ch = 'a' + val; + } else { + ch = 'a'; + } + } +} + +} // namespace + +namespace crx_file { +namespace id_util { + +// First 16 bytes of SHA256 hashed public key. +const size_t kIdSize = 16; + +std::string GenerateId(base::StringPiece input) { + uint8_t hash[kIdSize]; + crypto::SHA256HashString(input, hash, sizeof(hash)); + return GenerateIdFromHash(hash, sizeof(hash)); +} + +std::string GenerateIdFromHash(const uint8_t* hash, size_t hash_size) { + CHECK_GE(hash_size, kIdSize); + std::string result = base::HexEncode(hash, kIdSize); + ConvertHexadecimalToIDAlphabet(&result); + return result; +} + +std::string GenerateIdFromHex(const std::string& input) { + std::string output = input; + ConvertHexadecimalToIDAlphabet(&output); + return output; +} + +std::string GenerateIdForPath(const base::FilePath& path) { + base::FilePath new_path = MaybeNormalizePath(path); + const base::StringPiece path_bytes( + reinterpret_cast<const char*>(new_path.value().data()), + new_path.value().size() * sizeof(base::FilePath::CharType)); + return GenerateId(path_bytes); +} + +std::string HashedIdInHex(const std::string& id) { + const std::string id_hash = base::SHA1HashString(id); + DCHECK_EQ(base::kSHA1Length, id_hash.length()); + return base::HexEncode(id_hash.c_str(), id_hash.length()); +} + +base::FilePath MaybeNormalizePath(const base::FilePath& path) { +#if defined(OS_WIN) + // Normalize any drive letter to upper-case. We do this for consistency with + // net_utils::FilePathToFileURL(), which does the same thing, to make string + // comparisons simpler. + base::FilePath::StringType path_str = path.value(); + if (path_str.size() >= 2 && path_str[0] >= L'a' && path_str[0] <= L'z' && + path_str[1] == L':') + path_str[0] = towupper(path_str[0]); + + return base::FilePath(path_str); +#else + return path; +#endif +} + +bool IdIsValid(const std::string& id) { + // Verify that the id is legal. + if (id.size() != (crx_file::id_util::kIdSize * 2)) + return false; + + for (size_t i = 0; i < id.size(); i++) { + const char ch = base::ToLowerASCII(id[i]); + if (ch < 'a' || ch > 'p') + return false; + } + + return true; +} + +} // namespace id_util +} // namespace crx_file
diff --git a/src/updater_components/crx_file/id_util.h b/src/updater_components/crx_file/id_util.h new file mode 100644 index 0000000..6370560 --- /dev/null +++ b/src/updater_components/crx_file/id_util.h
@@ -0,0 +1,55 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRX_FILE_ID_UTIL_H_ +#define COMPONENTS_CRX_FILE_ID_UTIL_H_ + +#include "base/strings/string_piece.h" + +#include <stddef.h> +#include <stdint.h> + +#include <string> + +namespace base { +class FilePath; +} + +namespace crx_file { +namespace id_util { + +// The number of bytes in a legal id. +extern const size_t kIdSize; + +// Generates an extension ID from arbitrary input. The same input string will +// always generate the same output ID. +std::string GenerateId(base::StringPiece input); + +// Generates an ID from a HEX string. The same input string will always generate +// the same output ID. +std::string GenerateIdFromHex(const std::string& input); + +// Generates an ID from the first |kIdSize| bytes of a SHA256 hash. +// |hash_size| must be at least |kIdSize|. +std::string GenerateIdFromHash(const uint8_t* hash, size_t hash_size); + +// Generates an ID for an extension in the given path. +// Used while developing extensions, before they have a key. +std::string GenerateIdForPath(const base::FilePath& path); + +// Returns the hash of an extension ID in hex. +std::string HashedIdInHex(const std::string& id); + +// Normalizes the path for use by the extension. On Windows, this will make +// sure the drive letter is uppercase. +base::FilePath MaybeNormalizePath(const base::FilePath& path); + +// Checks if |id| is a valid extension-id. Extension-ids are used for anything +// that comes in a CRX file, including apps, extensions, and components. +bool IdIsValid(const std::string& id); + +} // namespace id_util +} // namespace crx_file + +#endif // COMPONENTS_CRX_FILE_ID_UTIL_H_
diff --git a/src/updater_components/crx_file/id_util_unittest.cc b/src/updater_components/crx_file/id_util_unittest.cc new file mode 100644 index 0000000..ee31110 --- /dev/null +++ b/src/updater_components/crx_file/id_util_unittest.cc
@@ -0,0 +1,56 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crx_file/id_util.h" + +#include <stdint.h> + +#include "base/stl_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace crx_file { +namespace id_util { + +TEST(IDUtilTest, GenerateID) { + const uint8_t public_key_info[] = { + 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, + 0x89, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, 0x20, 0xdc, 0x7c, 0x9b, + 0x0c, 0xdc, 0x51, 0x61, 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08, + 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, 0x85, 0x7b, 0x0c, 0x04, + 0x13, 0x3f, 0x8d, 0xf4, 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a, + 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, 0x9e, 0x36, 0x74, 0x30, + 0xda, 0x8a, 0x31, 0x4f, 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17, + 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, 0xb2, 0x65, 0x7a, 0x89, + 0x4e, 0xb6, 0x47, 0xff, 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85, + 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, 0x96, 0xd0, 0xd6, 0x14, + 0x6f, 0x13, 0x8d, 0xc5, 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18, + 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, 0xe7, 0x1f, 0x0f, 0xe6, + 0x0f, 0x02, 0x03, 0x01, 0x00, 0x01}; + std::string extension_id = + GenerateId(std::string(reinterpret_cast<const char*>(&public_key_info[0]), + base::size(public_key_info))); + EXPECT_EQ("melddjfinppjdikinhbgehiennejpfhp", extension_id); + + EXPECT_EQ("daibjpdaanagajckigeiigphanababab", + GenerateIdFromHash(public_key_info, sizeof(public_key_info))); + + EXPECT_EQ("jpignaibiiemhngfjkcpokkamffknabf", GenerateId("test")); + + EXPECT_EQ("ncocknphbhhlhkikpnnlmbcnbgdempcd", GenerateId("_")); + + EXPECT_EQ("a", GenerateIdFromHex("_")); + + EXPECT_EQ( + "bjbdkfoakgmkndalgpadobhgbhhoanhongcmfnghaakjmggnkffgnhmdpfngkeho", + GenerateIdFromHex( + "1913a5e0a6cad30b6f03e176177e0d7ed62c5d6700a9c66da556d7c3f5d6a47e")); + + EXPECT_EQ( + "jimneklojkjdibfkgiiophfhjhbdgcfi", + GenerateId("this_string_is_longer_than_a_single_sha256_hash_digest")); +} + +} // namespace id_util +} // namespace crx_file
diff --git a/src/updater_components/prefs/BUILD.gn b/src/updater_components/prefs/BUILD.gn new file mode 100644 index 0000000..1954a7f --- /dev/null +++ b/src/updater_components/prefs/BUILD.gn
@@ -0,0 +1,107 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +component("prefs") { + sources = [ + "command_line_pref_store.cc", + "command_line_pref_store.h", + "default_pref_store.cc", + "default_pref_store.h", + "in_memory_pref_store.cc", + "in_memory_pref_store.h", + "json_pref_store.cc", + "json_pref_store.h", + "overlay_user_pref_store.cc", + "overlay_user_pref_store.h", + "persistent_pref_store.cc", + "persistent_pref_store.h", + "pref_change_registrar.cc", + "pref_change_registrar.h", + "pref_filter.h", + "pref_member.cc", + "pref_member.h", + "pref_notifier.h", + "pref_notifier_impl.cc", + "pref_notifier_impl.h", + "pref_observer.h", + "pref_registry.cc", + "pref_registry.h", + "pref_registry_simple.cc", + "pref_registry_simple.h", + "pref_service.cc", + "pref_service.h", + "pref_service_factory.cc", + "pref_service_factory.h", + "pref_store.cc", + "pref_store.h", + "pref_value_map.cc", + "pref_value_map.h", + "pref_value_store.cc", + "pref_value_store.h", + "prefs_export.h", + "scoped_user_pref_update.cc", + "scoped_user_pref_update.h", + "value_map_pref_store.cc", + "value_map_pref_store.h", + "writeable_pref_store.cc", + "writeable_pref_store.h", + ] + + defines = [ "COMPONENTS_PREFS_IMPLEMENTATION" ] + + deps = [ + "//base", + "//base/util/values:values_util", + ] +} + +static_library("test_support") { + testonly = true + sources = [ + "mock_pref_change_callback.cc", + "mock_pref_change_callback.h", + "pref_store_observer_mock.cc", + "pref_store_observer_mock.h", + "testing_pref_service.cc", + "testing_pref_service.h", + "testing_pref_store.cc", + "testing_pref_store.h", + ] + + public_deps = [ + ":prefs", + ] + deps = [ + "//base", + "//testing/gmock", + "//testing/gtest", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "default_pref_store_unittest.cc", + "in_memory_pref_store_unittest.cc", + "json_pref_store_unittest.cc", + "overlay_user_pref_store_unittest.cc", + "persistent_pref_store_unittest.cc", + "persistent_pref_store_unittest.h", + "pref_change_registrar_unittest.cc", + "pref_member_unittest.cc", + "pref_notifier_impl_unittest.cc", + "pref_service_unittest.cc", + "pref_value_map_unittest.cc", + "pref_value_store_unittest.cc", + "scoped_user_pref_update_unittest.cc", + ] + + deps = [ + ":test_support", + "//base", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] +}
diff --git a/src/updater_components/prefs/command_line_pref_store.cc b/src/updater_components/prefs/command_line_pref_store.cc new file mode 100644 index 0000000..79fc10c --- /dev/null +++ b/src/updater_components/prefs/command_line_pref_store.cc
@@ -0,0 +1,77 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/command_line_pref_store.h" + +#include <memory> +#include <string> + +#include "base/files/file_path.h" +#include "base/strings/string_number_conversions.h" + +CommandLinePrefStore::CommandLinePrefStore( + const base::CommandLine* command_line) + : command_line_(command_line) {} + +CommandLinePrefStore::~CommandLinePrefStore() {} + +void CommandLinePrefStore::ApplyStringSwitches( + const CommandLinePrefStore::SwitchToPreferenceMapEntry string_switch[], + size_t size) { + for (size_t i = 0; i < size; ++i) { + if (command_line_->HasSwitch(string_switch[i].switch_name)) { + SetValue(string_switch[i].preference_path, + std::make_unique<base::Value>(command_line_->GetSwitchValueASCII( + string_switch[i].switch_name)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } + } +} + +void CommandLinePrefStore::ApplyPathSwitches( + const CommandLinePrefStore::SwitchToPreferenceMapEntry path_switch[], + size_t size) { + for (size_t i = 0; i < size; ++i) { + if (command_line_->HasSwitch(path_switch[i].switch_name)) { + SetValue(path_switch[i].preference_path, + std::make_unique<base::Value>( + command_line_->GetSwitchValuePath(path_switch[i].switch_name) + .value()), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } + } +} + +void CommandLinePrefStore::ApplyIntegerSwitches( + const CommandLinePrefStore::SwitchToPreferenceMapEntry integer_switch[], + size_t size) { + for (size_t i = 0; i < size; ++i) { + if (command_line_->HasSwitch(integer_switch[i].switch_name)) { + std::string str_value = command_line_->GetSwitchValueASCII( + integer_switch[i].switch_name); + int int_value = 0; + if (!base::StringToInt(str_value, &int_value)) { + LOG(ERROR) << "The value " << str_value << " of " + << integer_switch[i].switch_name + << " can not be converted to integer, ignoring!"; + continue; + } + SetValue(integer_switch[i].preference_path, + std::make_unique<base::Value>(int_value), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } + } +} + +void CommandLinePrefStore::ApplyBooleanSwitches( + const CommandLinePrefStore::BooleanSwitchToPreferenceMapEntry + boolean_switch_map[], size_t size) { + for (size_t i = 0; i < size; ++i) { + if (command_line_->HasSwitch(boolean_switch_map[i].switch_name)) { + SetValue(boolean_switch_map[i].preference_path, + std::make_unique<base::Value>(boolean_switch_map[i].set_value), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } + } +}
diff --git a/src/updater_components/prefs/command_line_pref_store.h b/src/updater_components/prefs/command_line_pref_store.h new file mode 100644 index 0000000..03d5da9 --- /dev/null +++ b/src/updater_components/prefs/command_line_pref_store.h
@@ -0,0 +1,66 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_COMMAND_LINE_PREF_STORE_H_ +#define COMPONENTS_PREFS_COMMAND_LINE_PREF_STORE_H_ + +#include "base/command_line.h" +#include "base/macros.h" +#include "base/values.h" +#include "components/prefs/value_map_pref_store.h" + +// Base class for a PrefStore that maps command line switches to preferences. +// The Apply...Switches() methods can be called by subclasses with their own +// maps, or delegated to other code. +class COMPONENTS_PREFS_EXPORT CommandLinePrefStore : public ValueMapPrefStore { + public: + struct SwitchToPreferenceMapEntry { + const char* switch_name; + const char* preference_path; + }; + + // |set_value| indicates what the preference should be set to if the switch + // is present. + struct BooleanSwitchToPreferenceMapEntry { + const char* switch_name; + const char* preference_path; + bool set_value; + }; + + // Apply command-line switches to the corresponding preferences of the switch + // map, where the value associated with the switch is a string. + void ApplyStringSwitches( + const SwitchToPreferenceMapEntry string_switch_map[], size_t size); + + // Apply command-line switches to the corresponding preferences of the switch + // map, where the value associated with the switch is a path. + void ApplyPathSwitches(const SwitchToPreferenceMapEntry path_switch_map[], + size_t size); + + // Apply command-line switches to the corresponding preferences of the switch + // map, where the value associated with the switch is an integer. + void ApplyIntegerSwitches( + const SwitchToPreferenceMapEntry integer_switch_map[], size_t size); + + // Apply command-line switches to the corresponding preferences of the + // boolean switch map. + void ApplyBooleanSwitches( + const BooleanSwitchToPreferenceMapEntry boolean_switch_map[], + size_t size); + + + protected: + explicit CommandLinePrefStore(const base::CommandLine* command_line); + ~CommandLinePrefStore() override; + + const base::CommandLine* command_line() { return command_line_; } + + private: + // Weak reference. + const base::CommandLine* command_line_; + + DISALLOW_COPY_AND_ASSIGN(CommandLinePrefStore); +}; + +#endif // COMPONENTS_PREFS_COMMAND_LINE_PREF_STORE_H_
diff --git a/src/updater_components/prefs/default_pref_store.cc b/src/updater_components/prefs/default_pref_store.cc new file mode 100644 index 0000000..d8d13ec --- /dev/null +++ b/src/updater_components/prefs/default_pref_store.cc
@@ -0,0 +1,59 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/default_pref_store.h" + +#include <utility> + +#include "base/logging.h" + +using base::Value; + +DefaultPrefStore::DefaultPrefStore() {} + +bool DefaultPrefStore::GetValue(const std::string& key, + const Value** result) const { + return prefs_.GetValue(key, result); +} + +std::unique_ptr<base::DictionaryValue> DefaultPrefStore::GetValues() const { + return prefs_.AsDictionaryValue(); +} + +void DefaultPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void DefaultPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool DefaultPrefStore::HasObservers() const { + return observers_.might_have_observers(); +} + +void DefaultPrefStore::SetDefaultValue(const std::string& key, Value value) { + DCHECK(!GetValue(key, nullptr)); + prefs_.SetValue(key, std::move(value)); +} + +void DefaultPrefStore::ReplaceDefaultValue(const std::string& key, + Value value) { + DCHECK(GetValue(key, nullptr)); + bool notify = prefs_.SetValue(key, std::move(value)); + if (notify) { + for (Observer& observer : observers_) + observer.OnPrefValueChanged(key); + } +} + +DefaultPrefStore::const_iterator DefaultPrefStore::begin() const { + return prefs_.begin(); +} + +DefaultPrefStore::const_iterator DefaultPrefStore::end() const { + return prefs_.end(); +} + +DefaultPrefStore::~DefaultPrefStore() {}
diff --git a/src/updater_components/prefs/default_pref_store.h b/src/updater_components/prefs/default_pref_store.h new file mode 100644 index 0000000..17ba3ec --- /dev/null +++ b/src/updater_components/prefs/default_pref_store.h
@@ -0,0 +1,54 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_ +#define COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_ + +#include <memory> +#include <string> + +#include "base/macros.h" +#include "base/observer_list.h" +#include "base/values.h" +#include "components/prefs/pref_store.h" +#include "components/prefs/pref_value_map.h" +#include "components/prefs/prefs_export.h" + +// Used within a PrefRegistry to keep track of default preference values. +class COMPONENTS_PREFS_EXPORT DefaultPrefStore : public PrefStore { + public: + typedef PrefValueMap::const_iterator const_iterator; + + DefaultPrefStore(); + + // PrefStore implementation: + bool GetValue(const std::string& key, + const base::Value** result) const override; + std::unique_ptr<base::DictionaryValue> GetValues() const override; + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + + // Sets a |value| for |key|. Should only be called if a value has not been + // set yet; otherwise call ReplaceDefaultValue(). + void SetDefaultValue(const std::string& key, base::Value value); + + // Replaces the the value for |key| with a new value. Should only be called + // if a value has alreday been set; otherwise call SetDefaultValue(). + void ReplaceDefaultValue(const std::string& key, base::Value value); + + const_iterator begin() const; + const_iterator end() const; + + private: + ~DefaultPrefStore() override; + + PrefValueMap prefs_; + + base::ObserverList<PrefStore::Observer, true>::Unchecked observers_; + + DISALLOW_COPY_AND_ASSIGN(DefaultPrefStore); +}; + +#endif // COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_
diff --git a/src/updater_components/prefs/default_pref_store_unittest.cc b/src/updater_components/prefs/default_pref_store_unittest.cc new file mode 100644 index 0000000..bc40c4c --- /dev/null +++ b/src/updater_components/prefs/default_pref_store_unittest.cc
@@ -0,0 +1,66 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/macros.h" +#include "components/prefs/default_pref_store.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Value; + +namespace { + +class MockPrefStoreObserver : public PrefStore::Observer { + public: + explicit MockPrefStoreObserver(DefaultPrefStore* pref_store); + ~MockPrefStoreObserver() override; + + int change_count() { + return change_count_; + } + + // PrefStore::Observer implementation: + void OnPrefValueChanged(const std::string& key) override; + void OnInitializationCompleted(bool succeeded) override {} + + private: + DefaultPrefStore* pref_store_; + + int change_count_; + + DISALLOW_COPY_AND_ASSIGN(MockPrefStoreObserver); +}; + +MockPrefStoreObserver::MockPrefStoreObserver(DefaultPrefStore* pref_store) + : pref_store_(pref_store), change_count_(0) { + pref_store_->AddObserver(this); +} + +MockPrefStoreObserver::~MockPrefStoreObserver() { + pref_store_->RemoveObserver(this); +} + +void MockPrefStoreObserver::OnPrefValueChanged(const std::string& key) { + change_count_++; +} + +} // namespace + +TEST(DefaultPrefStoreTest, NotifyPrefValueChanged) { + scoped_refptr<DefaultPrefStore> pref_store(new DefaultPrefStore); + MockPrefStoreObserver observer(pref_store.get()); + std::string kPrefKey("pref_key"); + + // Setting a default value shouldn't send a change notification. + pref_store->SetDefaultValue(kPrefKey, Value("foo")); + EXPECT_EQ(0, observer.change_count()); + + // Replacing the default value should send a change notification... + pref_store->ReplaceDefaultValue(kPrefKey, Value("bar")); + EXPECT_EQ(1, observer.change_count()); + + // But only if the value actually changed. + pref_store->ReplaceDefaultValue(kPrefKey, Value("bar")); + EXPECT_EQ(1, observer.change_count()); +} +
diff --git a/src/updater_components/prefs/in_memory_pref_store.cc b/src/updater_components/prefs/in_memory_pref_store.cc new file mode 100644 index 0000000..20d1eea --- /dev/null +++ b/src/updater_components/prefs/in_memory_pref_store.cc
@@ -0,0 +1,86 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/in_memory_pref_store.h" + +#include <memory> +#include <utility> + +#include "base/values.h" + +InMemoryPrefStore::InMemoryPrefStore() {} + +InMemoryPrefStore::~InMemoryPrefStore() {} + +bool InMemoryPrefStore::GetValue(const std::string& key, + const base::Value** value) const { + return prefs_.GetValue(key, value); +} + +std::unique_ptr<base::DictionaryValue> InMemoryPrefStore::GetValues() const { + return prefs_.AsDictionaryValue(); +} + +bool InMemoryPrefStore::GetMutableValue(const std::string& key, + base::Value** value) { + return prefs_.GetValue(key, value); +} + +void InMemoryPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void InMemoryPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool InMemoryPrefStore::HasObservers() const { + return observers_.might_have_observers(); +} + +bool InMemoryPrefStore::IsInitializationComplete() const { + return true; +} + +void InMemoryPrefStore::SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + DCHECK(value); + if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)))) + ReportValueChanged(key, flags); +} + +void InMemoryPrefStore::SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + DCHECK(value); + prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value))); +} + +void InMemoryPrefStore::RemoveValue(const std::string& key, uint32_t flags) { + if (prefs_.RemoveValue(key)) + ReportValueChanged(key, flags); +} + +bool InMemoryPrefStore::ReadOnly() const { + return false; +} + +PersistentPrefStore::PrefReadError InMemoryPrefStore::GetReadError() const { + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +PersistentPrefStore::PrefReadError InMemoryPrefStore::ReadPrefs() { + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +void InMemoryPrefStore::ReportValueChanged(const std::string& key, + uint32_t flags) { + for (Observer& observer : observers_) + observer.OnPrefValueChanged(key); +} + +bool InMemoryPrefStore::IsInMemoryPrefStore() const { + return true; +}
diff --git a/src/updater_components/prefs/in_memory_pref_store.h b/src/updater_components/prefs/in_memory_pref_store.h new file mode 100644 index 0000000..c623adb --- /dev/null +++ b/src/updater_components/prefs/in_memory_pref_store.h
@@ -0,0 +1,66 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_IN_MEMORY_PREF_STORE_H_ +#define COMPONENTS_PREFS_IN_MEMORY_PREF_STORE_H_ + +#include <stdint.h> + +#include <string> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/observer_list.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/prefs/pref_value_map.h" + +// A light-weight prefstore implementation that keeps preferences +// in a memory backed store. This is not a persistent prefstore -- we +// subclass the PersistentPrefStore here since it is needed by the +// PrefService, which in turn is needed by various components. +class COMPONENTS_PREFS_EXPORT InMemoryPrefStore : public PersistentPrefStore { + public: + InMemoryPrefStore(); + + // PrefStore implementation. + bool GetValue(const std::string& key, + const base::Value** result) const override; + std::unique_ptr<base::DictionaryValue> GetValues() const override; + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + bool IsInitializationComplete() const override; + + // PersistentPrefStore implementation. + bool GetMutableValue(const std::string& key, base::Value** result) override; + void ReportValueChanged(const std::string& key, uint32_t flags) override; + void SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + void SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + void RemoveValue(const std::string& key, uint32_t flags) override; + bool ReadOnly() const override; + PrefReadError GetReadError() const override; + PersistentPrefStore::PrefReadError ReadPrefs() override; + void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override {} + void SchedulePendingLossyWrites() override {} + void ClearMutableValues() override {} + void OnStoreDeletionFromDisk() override {} + bool IsInMemoryPrefStore() const override; + + protected: + ~InMemoryPrefStore() override; + + private: + // Stores the preference values. + PrefValueMap prefs_; + + base::ObserverList<PrefStore::Observer, true>::Unchecked observers_; + + DISALLOW_COPY_AND_ASSIGN(InMemoryPrefStore); +}; + +#endif // COMPONENTS_PREFS_IN_MEMORY_PREF_STORE_H_
diff --git a/src/updater_components/prefs/in_memory_pref_store_unittest.cc b/src/updater_components/prefs/in_memory_pref_store_unittest.cc new file mode 100644 index 0000000..dffe5fa --- /dev/null +++ b/src/updater_components/prefs/in_memory_pref_store_unittest.cc
@@ -0,0 +1,113 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/in_memory_pref_store.h" + +#include <memory> + +#include "base/test/task_environment.h" +#include "base/values.h" +#include "components/prefs/persistent_pref_store_unittest.h" +#include "components/prefs/pref_store_observer_mock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const char kTestPref[] = "test.pref"; + +class InMemoryPrefStoreTest : public testing::Test { + public: + InMemoryPrefStoreTest() { } + + void SetUp() override { store_ = new InMemoryPrefStore(); } + protected: + base::test::TaskEnvironment task_environment_; + scoped_refptr<InMemoryPrefStore> store_; + PrefStoreObserverMock observer_; +}; + +TEST_F(InMemoryPrefStoreTest, SetGetValue) { + const base::Value* value = nullptr; + base::Value* mutable_value = nullptr; + EXPECT_FALSE(store_->GetValue(kTestPref, &value)); + EXPECT_FALSE(store_->GetMutableValue(kTestPref, &mutable_value)); + + store_->SetValue(kTestPref, std::make_unique<base::Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(store_->GetValue(kTestPref, &value)); + EXPECT_TRUE(base::Value(42).Equals(value)); + EXPECT_TRUE(store_->GetMutableValue(kTestPref, &mutable_value)); + EXPECT_TRUE(base::Value(42).Equals(mutable_value)); + + store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_FALSE(store_->GetValue(kTestPref, &value)); + EXPECT_FALSE(store_->GetMutableValue(kTestPref, &mutable_value)); +} + +TEST_F(InMemoryPrefStoreTest, GetSetObserver) { + // Starts with no observers. + EXPECT_FALSE(store_->HasObservers()); + + // Add one. + store_->AddObserver(&observer_); + EXPECT_TRUE(store_->HasObservers()); + + // Remove only observer. + store_->RemoveObserver(&observer_); + EXPECT_FALSE(store_->HasObservers()); +} + +TEST_F(InMemoryPrefStoreTest, CallObserver) { + // With observer included. + store_->AddObserver(&observer_); + + // Triggers on SetValue. + store_->SetValue(kTestPref, std::make_unique<base::Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + observer_.VerifyAndResetChangedKey(kTestPref); + + // And RemoveValue. + store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + observer_.VerifyAndResetChangedKey(kTestPref); + + // But not SetValueSilently. + store_->SetValueSilently(kTestPref, std::make_unique<base::Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_EQ(0u, observer_.changed_keys.size()); + + // On multiple RemoveValues only the first one triggers observer. + store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + observer_.VerifyAndResetChangedKey(kTestPref); + store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_EQ(0u, observer_.changed_keys.size()); + + // Doesn't make call on removed observers. + store_->RemoveObserver(&observer_); + store_->SetValue(kTestPref, std::make_unique<base::Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_EQ(0u, observer_.changed_keys.size()); +} + +TEST_F(InMemoryPrefStoreTest, Initialization) { + EXPECT_TRUE(store_->IsInitializationComplete()); +} + +TEST_F(InMemoryPrefStoreTest, ReadOnly) { + EXPECT_FALSE(store_->ReadOnly()); +} + +TEST_F(InMemoryPrefStoreTest, GetReadError) { + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, store_->GetReadError()); +} + +TEST_F(InMemoryPrefStoreTest, ReadPrefs) { + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, store_->ReadPrefs()); +} + +TEST_F(InMemoryPrefStoreTest, CommitPendingWriteWithCallback) { + TestCommitPendingWriteWithCallback(store_.get(), &task_environment_); +} + +} // namespace
diff --git a/src/updater_components/prefs/ios/BUILD.gn b/src/updater_components/prefs/ios/BUILD.gn new file mode 100644 index 0000000..82aa654 --- /dev/null +++ b/src/updater_components/prefs/ios/BUILD.gn
@@ -0,0 +1,15 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("ios") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ + "pref_observer_bridge.h", + "pref_observer_bridge.mm", + ] + deps = [ + "//base", + "//components/prefs", + ] +}
diff --git a/src/updater_components/prefs/ios/pref_observer_bridge.h b/src/updater_components/prefs/ios/pref_observer_bridge.h new file mode 100644 index 0000000..425ef87 --- /dev/null +++ b/src/updater_components/prefs/ios/pref_observer_bridge.h
@@ -0,0 +1,32 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_IOS_PREF_OBSERVER_BRIDGE_H_ +#define COMPONENTS_PREFS_IOS_PREF_OBSERVER_BRIDGE_H_ + +#import <Foundation/Foundation.h> + +#include <string> + +class PrefChangeRegistrar; + +@protocol PrefObserverDelegate +- (void)onPreferenceChanged:(const std::string&)preferenceName; +@end + +class PrefObserverBridge { + public: + explicit PrefObserverBridge(id<PrefObserverDelegate> delegate); + virtual ~PrefObserverBridge(); + + virtual void ObserveChangesForPreference(const std::string& pref_name, + PrefChangeRegistrar* registrar); + + private: + virtual void OnPreferenceChanged(const std::string& pref_name); + + __weak id<PrefObserverDelegate> delegate_ = nil; +}; + +#endif // COMPONENTS_PREFS_IOS_PREF_OBSERVER_BRIDGE_H_
diff --git a/src/updater_components/prefs/ios/pref_observer_bridge.mm b/src/updater_components/prefs/ios/pref_observer_bridge.mm new file mode 100644 index 0000000..852309f --- /dev/null +++ b/src/updater_components/prefs/ios/pref_observer_bridge.mm
@@ -0,0 +1,29 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "components/prefs/ios/pref_observer_bridge.h" + +#include "base/bind.h" +#include "components/prefs/pref_change_registrar.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +PrefObserverBridge::PrefObserverBridge(id<PrefObserverDelegate> delegate) + : delegate_(delegate) {} + +PrefObserverBridge::~PrefObserverBridge() {} + +void PrefObserverBridge::ObserveChangesForPreference( + const std::string& pref_name, + PrefChangeRegistrar* registrar) { + PrefChangeRegistrar::NamedChangeCallback callback = base::BindRepeating( + &PrefObserverBridge::OnPreferenceChanged, base::Unretained(this)); + registrar->Add(pref_name.c_str(), callback); +} + +void PrefObserverBridge::OnPreferenceChanged(const std::string& pref_name) { + [delegate_ onPreferenceChanged:pref_name]; +}
diff --git a/src/updater_components/prefs/json_pref_store.cc b/src/updater_components/prefs/json_pref_store.cc new file mode 100644 index 0000000..0dcdb17 --- /dev/null +++ b/src/updater_components/prefs/json_pref_store.cc
@@ -0,0 +1,519 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/json_pref_store.h" + +#include <stddef.h> + +#include <algorithm> +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/json/json_file_value_serializer.h" +#include "base/json/json_string_value_serializer.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/metrics/histogram.h" +#include "base/sequenced_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/task_runner_util.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/time/default_clock.h" +#include "base/values.h" +#include "components/prefs/pref_filter.h" + +// Result returned from internal read tasks. +struct JsonPrefStore::ReadResult { + public: + ReadResult(); + ~ReadResult(); + + std::unique_ptr<base::Value> value; + PrefReadError error; + bool no_dir; + + private: + DISALLOW_COPY_AND_ASSIGN(ReadResult); +}; + +JsonPrefStore::ReadResult::ReadResult() + : error(PersistentPrefStore::PREF_READ_ERROR_NONE), no_dir(false) { +} + +JsonPrefStore::ReadResult::~ReadResult() { +} + +namespace { + +// Some extensions we'll tack on to copies of the Preferences files. +const base::FilePath::CharType kBadExtension[] = FILE_PATH_LITERAL("bad"); + +PersistentPrefStore::PrefReadError HandleReadErrors( + const base::Value* value, + const base::FilePath& path, + int error_code, + const std::string& error_msg) { + if (!value) { + DVLOG(1) << "Error while loading JSON file: " << error_msg + << ", file: " << path.value(); + switch (error_code) { + case JSONFileValueDeserializer::JSON_ACCESS_DENIED: + return PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED; + case JSONFileValueDeserializer::JSON_CANNOT_READ_FILE: + return PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER; + case JSONFileValueDeserializer::JSON_FILE_LOCKED: + return PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED; + case JSONFileValueDeserializer::JSON_NO_SUCH_FILE: + return PersistentPrefStore::PREF_READ_ERROR_NO_FILE; + default: + // JSON errors indicate file corruption of some sort. + // Since the file is corrupt, move it to the side and continue with + // empty preferences. This will result in them losing their settings. + // We keep the old file for possible support and debugging assistance + // as well as to detect if they're seeing these errors repeatedly. + // TODO(erikkay) Instead, use the last known good file. + base::FilePath bad = path.ReplaceExtension(kBadExtension); + + // If they've ever had a parse error before, put them in another bucket. + // TODO(erikkay) if we keep this error checking for very long, we may + // want to differentiate between recent and long ago errors. + bool bad_existed = base::PathExists(bad); + base::Move(path, bad); + return bad_existed ? PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT + : PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE; + } + } + if (!value->is_dict()) + return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE; + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +// Records a sample for |size| in the Settings.JsonDataReadSizeKilobytes +// histogram suffixed with the base name of the JSON file under |path|. +void RecordJsonDataSizeHistogram(const base::FilePath& path, size_t size) { + std::string spaceless_basename; + base::ReplaceChars(path.BaseName().MaybeAsASCII(), " ", "_", + &spaceless_basename); + + // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS + // macro adapted to allow for a dynamically suffixed histogram name. + // Note: The factory creates and owns the histogram. + // This histogram is expired but the code was intentionally left behind so + // it can be re-enabled on Stable in a single config tweak if needed. + base::HistogramBase* histogram = base::Histogram::FactoryGet( + "Settings.JsonDataReadSizeKilobytes." + spaceless_basename, 1, 10000, 50, + base::HistogramBase::kUmaTargetedHistogramFlag); + histogram->Add(static_cast<int>(size) / 1024); +} + +std::unique_ptr<JsonPrefStore::ReadResult> ReadPrefsFromDisk( + const base::FilePath& path) { + int error_code; + std::string error_msg; + std::unique_ptr<JsonPrefStore::ReadResult> read_result( + new JsonPrefStore::ReadResult); + JSONFileValueDeserializer deserializer(path); + read_result->value = deserializer.Deserialize(&error_code, &error_msg); + read_result->error = + HandleReadErrors(read_result->value.get(), path, error_code, error_msg); + read_result->no_dir = !base::PathExists(path.DirName()); + + if (read_result->error == PersistentPrefStore::PREF_READ_ERROR_NONE) + RecordJsonDataSizeHistogram(path, deserializer.get_last_read_size()); + + return read_result; +} + +} // namespace + +JsonPrefStore::JsonPrefStore( + const base::FilePath& pref_filename, + std::unique_ptr<PrefFilter> pref_filter, + scoped_refptr<base::SequencedTaskRunner> file_task_runner) + : path_(pref_filename), + file_task_runner_(std::move(file_task_runner)), + prefs_(new base::DictionaryValue()), + read_only_(false), + writer_(pref_filename, file_task_runner_), + pref_filter_(std::move(pref_filter)), + initialized_(false), + filtering_in_progress_(false), + pending_lossy_write_(false), + read_error_(PREF_READ_ERROR_NONE), + has_pending_write_reply_(false) { + DCHECK(!path_.empty()); +} + +bool JsonPrefStore::GetValue(const std::string& key, + const base::Value** result) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + base::Value* tmp = nullptr; + if (!prefs_->Get(key, &tmp)) + return false; + + if (result) + *result = tmp; + return true; +} + +std::unique_ptr<base::DictionaryValue> JsonPrefStore::GetValues() const { + return prefs_->CreateDeepCopy(); +} + +void JsonPrefStore::AddObserver(PrefStore::Observer* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + observers_.AddObserver(observer); +} + +void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + observers_.RemoveObserver(observer); +} + +bool JsonPrefStore::HasObservers() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + return observers_.might_have_observers(); +} + +bool JsonPrefStore::IsInitializationComplete() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + return initialized_; +} + +bool JsonPrefStore::GetMutableValue(const std::string& key, + base::Value** result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + return prefs_->Get(key, result); +} + +void JsonPrefStore::SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + DCHECK(value); + base::Value* old_value = nullptr; + prefs_->Get(key, &old_value); + if (!old_value || !value->Equals(old_value)) { + prefs_->Set(key, std::move(value)); + ReportValueChanged(key, flags); + } +} + +void JsonPrefStore::SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + DCHECK(value); + base::Value* old_value = nullptr; + prefs_->Get(key, &old_value); + if (!old_value || !value->Equals(old_value)) { + prefs_->Set(key, std::move(value)); + ScheduleWrite(flags); + } +} + +void JsonPrefStore::RemoveValue(const std::string& key, uint32_t flags) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (prefs_->RemovePath(key, nullptr)) + ReportValueChanged(key, flags); +} + +void JsonPrefStore::RemoveValueSilently(const std::string& key, + uint32_t flags) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + prefs_->RemovePath(key, nullptr); + ScheduleWrite(flags); +} + +bool JsonPrefStore::ReadOnly() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + return read_only_; +} + +PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + return read_error_; +} + +PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + OnFileRead(ReadPrefsFromDisk(path_)); + return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE + : read_error_; +} + +void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + initialized_ = false; + error_delegate_.reset(error_delegate); + + // Weakly binds the read task so that it doesn't kick in during shutdown. + base::PostTaskAndReplyWithResult( + file_task_runner_.get(), FROM_HERE, base::Bind(&ReadPrefsFromDisk, path_), + base::Bind(&JsonPrefStore::OnFileRead, AsWeakPtr())); +} + +void JsonPrefStore::CommitPendingWrite( + base::OnceClosure reply_callback, + base::OnceClosure synchronous_done_callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Schedule a write for any lossy writes that are outstanding to ensure that + // they get flushed when this function is called. + SchedulePendingLossyWrites(); + + if (writer_.HasPendingWrite() && !read_only_) + writer_.DoScheduledWrite(); + + // Since disk operations occur on |file_task_runner_|, the reply of a task + // posted to |file_task_runner_| will run after currently pending disk + // operations. Also, by definition of PostTaskAndReply(), the reply (in the + // |reply_callback| case will run on the current sequence. + + if (synchronous_done_callback) { + file_task_runner_->PostTask(FROM_HERE, + std::move(synchronous_done_callback)); + } + + if (reply_callback) { + file_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(), + std::move(reply_callback)); + } +} + +void JsonPrefStore::SchedulePendingLossyWrites() { + if (pending_lossy_write_) + writer_.ScheduleWrite(this); +} + +void JsonPrefStore::ReportValueChanged(const std::string& key, uint32_t flags) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (pref_filter_) + pref_filter_->FilterUpdate(key); + + for (PrefStore::Observer& observer : observers_) + observer.OnPrefValueChanged(key); + + ScheduleWrite(flags); +} + +void JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback( + bool write_success) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + has_pending_write_reply_ = false; + if (!on_next_successful_write_reply_.is_null()) { + base::Closure on_successful_write = + std::move(on_next_successful_write_reply_); + if (write_success) { + on_successful_write.Run(); + } else { + RegisterOnNextSuccessfulWriteReply(on_successful_write); + } + } +} + +// static +void JsonPrefStore::PostWriteCallback( + const base::Callback<void(bool success)>& on_next_write_callback, + const base::Callback<void(bool success)>& on_next_write_reply, + scoped_refptr<base::SequencedTaskRunner> reply_task_runner, + bool write_success) { + if (!on_next_write_callback.is_null()) + on_next_write_callback.Run(write_success); + + // We can't run |on_next_write_reply| on the current thread. Bounce back to + // the |reply_task_runner| which is the correct sequenced thread. + reply_task_runner->PostTask( + FROM_HERE, base::BindOnce(on_next_write_reply, write_success)); +} + +void JsonPrefStore::RegisterOnNextSuccessfulWriteReply( + const base::Closure& on_next_successful_write_reply) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(on_next_successful_write_reply_.is_null()); + + on_next_successful_write_reply_ = on_next_successful_write_reply; + + // If there are pending callbacks, avoid erasing them; the reply will be used + // as we set |on_next_successful_write_reply_|. Otherwise, setup a reply with + // an empty callback. + if (!has_pending_write_reply_) { + has_pending_write_reply_ = true; + writer_.RegisterOnNextWriteCallbacks( + base::Closure(), + base::Bind( + &PostWriteCallback, base::Callback<void(bool success)>(), + base::Bind(&JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback, + AsWeakPtr()), + base::SequencedTaskRunnerHandle::Get())); + } +} + +void JsonPrefStore::RegisterOnNextWriteSynchronousCallbacks( + OnWriteCallbackPair callbacks) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + has_pending_write_reply_ = true; + + writer_.RegisterOnNextWriteCallbacks( + callbacks.first, + base::Bind( + &PostWriteCallback, callbacks.second, + base::Bind(&JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback, + AsWeakPtr()), + base::SequencedTaskRunnerHandle::Get())); +} + +void JsonPrefStore::ClearMutableValues() { + NOTIMPLEMENTED(); +} + +void JsonPrefStore::OnStoreDeletionFromDisk() { + if (pref_filter_) + pref_filter_->OnStoreDeletionFromDisk(); +} + +void JsonPrefStore::OnFileRead(std::unique_ptr<ReadResult> read_result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + DCHECK(read_result); + + std::unique_ptr<base::DictionaryValue> unfiltered_prefs( + new base::DictionaryValue); + + read_error_ = read_result->error; + + bool initialization_successful = !read_result->no_dir; + + if (initialization_successful) { + switch (read_error_) { + case PREF_READ_ERROR_ACCESS_DENIED: + case PREF_READ_ERROR_FILE_OTHER: + case PREF_READ_ERROR_FILE_LOCKED: + case PREF_READ_ERROR_JSON_TYPE: + case PREF_READ_ERROR_FILE_NOT_SPECIFIED: + read_only_ = true; + break; + case PREF_READ_ERROR_NONE: + DCHECK(read_result->value); + unfiltered_prefs.reset( + static_cast<base::DictionaryValue*>(read_result->value.release())); + break; + case PREF_READ_ERROR_NO_FILE: + // If the file just doesn't exist, maybe this is first run. In any case + // there's no harm in writing out default prefs in this case. + case PREF_READ_ERROR_JSON_PARSE: + case PREF_READ_ERROR_JSON_REPEAT: + break; + case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE: + // This is a special error code to be returned by ReadPrefs when it + // can't complete synchronously, it should never be returned by the read + // operation itself. + case PREF_READ_ERROR_MAX_ENUM: + NOTREACHED(); + break; + } + } + + if (pref_filter_) { + filtering_in_progress_ = true; + const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback( + base::Bind( + &JsonPrefStore::FinalizeFileRead, AsWeakPtr(), + initialization_successful)); + pref_filter_->FilterOnLoad(post_filter_on_load_callback, + std::move(unfiltered_prefs)); + } else { + FinalizeFileRead(initialization_successful, std::move(unfiltered_prefs), + false); + } +} + +JsonPrefStore::~JsonPrefStore() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CommitPendingWrite(); +} + +bool JsonPrefStore::SerializeData(std::string* output) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + pending_lossy_write_ = false; + + if (pref_filter_) { + OnWriteCallbackPair callbacks = + pref_filter_->FilterSerializeData(prefs_.get()); + if (!callbacks.first.is_null() || !callbacks.second.is_null()) + RegisterOnNextWriteSynchronousCallbacks(callbacks); + } + + JSONStringValueSerializer serializer(output); + // Not pretty-printing prefs shrinks pref file size by ~30%. To obtain + // readable prefs for debugging purposes, you can dump your prefs into any + // command-line or online JSON pretty printing tool. + serializer.set_pretty_print(false); + bool success = serializer.Serialize(*prefs_); + DCHECK(success); + return success; +} + +void JsonPrefStore::FinalizeFileRead( + bool initialization_successful, + std::unique_ptr<base::DictionaryValue> prefs, + bool schedule_write) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + filtering_in_progress_ = false; + + if (!initialization_successful) { + for (PrefStore::Observer& observer : observers_) + observer.OnInitializationCompleted(false); + return; + } + + prefs_ = std::move(prefs); + + initialized_ = true; + + if (schedule_write) + ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS); + + if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE) + error_delegate_->OnError(read_error_); + + for (PrefStore::Observer& observer : observers_) + observer.OnInitializationCompleted(true); + + return; +} + +void JsonPrefStore::ScheduleWrite(uint32_t flags) { + if (read_only_) + return; + + if (flags & LOSSY_PREF_WRITE_FLAG) + pending_lossy_write_ = true; + else + writer_.ScheduleWrite(this); +}
diff --git a/src/updater_components/prefs/json_pref_store.h b/src/updater_components/prefs/json_pref_store.h new file mode 100644 index 0000000..089ce0b --- /dev/null +++ b/src/updater_components/prefs/json_pref_store.h
@@ -0,0 +1,203 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_JSON_PREF_STORE_H_ +#define COMPONENTS_PREFS_JSON_PREF_STORE_H_ + +#include <stdint.h> + +#include <memory> +#include <set> +#include <string> + +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/files/important_file_writer.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "base/sequence_checker.h" +#include "base/task/post_task.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/prefs/pref_filter.h" +#include "components/prefs/prefs_export.h" + +class PrefFilter; + +namespace base { +class DictionaryValue; +class FilePath; +class JsonPrefStoreCallbackTest; +class JsonPrefStoreLossyWriteTest; +class SequencedTaskRunner; +class WriteCallbacksObserver; +class Value; +} + +// A writable PrefStore implementation that is used for user preferences. +class COMPONENTS_PREFS_EXPORT JsonPrefStore + : public PersistentPrefStore, + public base::ImportantFileWriter::DataSerializer, + public base::SupportsWeakPtr<JsonPrefStore> { + public: + struct ReadResult; + + // A pair of callbacks to call before and after the preference file is written + // to disk. + using OnWriteCallbackPair = + std::pair<base::Closure, base::Callback<void(bool success)>>; + + // |pref_filename| is the path to the file to read prefs from. It is incorrect + // to create multiple JsonPrefStore with the same |pref_filename|. + // |file_task_runner| is used for asynchronous reads and writes. It must + // have the base::TaskShutdownBehavior::BLOCK_SHUTDOWN and base::MayBlock() + // traits. Unless external tasks need to run on the same sequence as + // JsonPrefStore tasks, keep the default value. + // The initial read is done synchronously, the TaskPriority is thus only used + // for flushes to disks and BEST_EFFORT is therefore appropriate. Priority of + // remaining BEST_EFFORT+BLOCK_SHUTDOWN tasks is bumped by the ThreadPool on + // shutdown. However, some shutdown use cases happen without + // ThreadPoolInstance::Shutdown() (e.g. ChromeRestartRequest::Start() and + // BrowserProcessImpl::EndSession()) and we must thus unfortunately make this + // USER_VISIBLE until we solve https://crbug.com/747495 to allow bumping + // priority of a sequence on demand. + JsonPrefStore(const base::FilePath& pref_filename, + std::unique_ptr<PrefFilter> pref_filter = nullptr, + scoped_refptr<base::SequencedTaskRunner> file_task_runner = + base::CreateSequencedTaskRunner( + {base::ThreadPool(), base::MayBlock(), + base::TaskPriority::USER_VISIBLE, + base::TaskShutdownBehavior::BLOCK_SHUTDOWN})); + + // PrefStore overrides: + bool GetValue(const std::string& key, + const base::Value** result) const override; + std::unique_ptr<base::DictionaryValue> GetValues() const override; + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + bool IsInitializationComplete() const override; + + // PersistentPrefStore overrides: + bool GetMutableValue(const std::string& key, base::Value** result) override; + void SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + void SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + void RemoveValue(const std::string& key, uint32_t flags) override; + bool ReadOnly() const override; + PrefReadError GetReadError() const override; + // Note this method may be asynchronous if this instance has a |pref_filter_| + // in which case it will return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE. + // See details in pref_filter.h. + PrefReadError ReadPrefs() override; + void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; + void CommitPendingWrite( + base::OnceClosure reply_callback = base::OnceClosure(), + base::OnceClosure synchronous_done_callback = + base::OnceClosure()) override; + void SchedulePendingLossyWrites() override; + void ReportValueChanged(const std::string& key, uint32_t flags) override; + + // Just like RemoveValue(), but doesn't notify observers. Used when doing some + // cleanup that shouldn't otherwise alert observers. + void RemoveValueSilently(const std::string& key, uint32_t flags); + + // Registers |on_next_successful_write_reply| to be called once, on the next + // successful write event of |writer_|. + // |on_next_successful_write_reply| will be called on the thread from which + // this method is called and does not need to be thread safe. + void RegisterOnNextSuccessfulWriteReply( + const base::Closure& on_next_successful_write_reply); + + void ClearMutableValues() override; + + void OnStoreDeletionFromDisk() override; + + private: + friend class base::JsonPrefStoreCallbackTest; + friend class base::JsonPrefStoreLossyWriteTest; + friend class base::WriteCallbacksObserver; + + ~JsonPrefStore() override; + + // If |write_success| is true, runs |on_next_successful_write_|. + // Otherwise, re-registers |on_next_successful_write_|. + void RunOrScheduleNextSuccessfulWriteCallback(bool write_success); + + // Handles the result of a write with result |write_success|. Runs + // |on_next_write_callback| on the current thread and posts + // |on_next_write_reply| on |reply_task_runner|. + static void PostWriteCallback( + const base::Callback<void(bool success)>& on_next_write_callback, + const base::Callback<void(bool success)>& on_next_write_reply, + scoped_refptr<base::SequencedTaskRunner> reply_task_runner, + bool write_success); + + // Registers the |callbacks| pair to be called once synchronously before and + // after, respectively, the next write event of |writer_|. + // Both callbacks must be thread-safe. + void RegisterOnNextWriteSynchronousCallbacks(OnWriteCallbackPair callbacks); + + // This method is called after the JSON file has been read. It then hands + // |value| (or an empty dictionary in some read error cases) to the + // |pref_filter| if one is set. It also gives a callback pointing at + // FinalizeFileRead() to that |pref_filter_| which is then responsible for + // invoking it when done. If there is no |pref_filter_|, FinalizeFileRead() + // is invoked directly. + void OnFileRead(std::unique_ptr<ReadResult> read_result); + + // ImportantFileWriter::DataSerializer overrides: + bool SerializeData(std::string* output) override; + + // This method is called after the JSON file has been read and the result has + // potentially been intercepted and modified by |pref_filter_|. + // |initialization_successful| is pre-determined by OnFileRead() and should + // be used when reporting OnInitializationCompleted(). + // |schedule_write| indicates whether a write should be immediately scheduled + // (typically because the |pref_filter_| has already altered the |prefs|) -- + // this will be ignored if this store is read-only. + void FinalizeFileRead(bool initialization_successful, + std::unique_ptr<base::DictionaryValue> prefs, + bool schedule_write); + + // Schedule a write with the file writer as long as |flags| doesn't contain + // WriteablePrefStore::LOSSY_PREF_WRITE_FLAG. + void ScheduleWrite(uint32_t flags); + + const base::FilePath path_; + const scoped_refptr<base::SequencedTaskRunner> file_task_runner_; + + std::unique_ptr<base::DictionaryValue> prefs_; + + bool read_only_; + + // Helper for safely writing pref data. + base::ImportantFileWriter writer_; + + std::unique_ptr<PrefFilter> pref_filter_; + base::ObserverList<PrefStore::Observer, true>::Unchecked observers_; + + std::unique_ptr<ReadErrorDelegate> error_delegate_; + + bool initialized_; + bool filtering_in_progress_; + bool pending_lossy_write_; + PrefReadError read_error_; + + std::set<std::string> keys_need_empty_value_; + + bool has_pending_write_reply_ = true; + base::Closure on_next_successful_write_reply_; + + SEQUENCE_CHECKER(sequence_checker_); + + DISALLOW_COPY_AND_ASSIGN(JsonPrefStore); +}; + +#endif // COMPONENTS_PREFS_JSON_PREF_STORE_H_
diff --git a/src/updater_components/prefs/json_pref_store_unittest.cc b/src/updater_components/prefs/json_pref_store_unittest.cc new file mode 100644 index 0000000..c747c36 --- /dev/null +++ b/src/updater_components/prefs/json_pref_store_unittest.cc
@@ -0,0 +1,985 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/json_pref_store.h" + +#include <stdint.h> + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/location.h" +#include "base/memory/ref_counted.h" +#include "base/metrics/histogram_samples.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/task_environment.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/threading/thread.h" +#include "base/values.h" +#include "components/prefs/persistent_pref_store_unittest.h" +#include "components/prefs/pref_filter.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +const char kHomePage[] = "homepage"; + +const char kReadJson[] = + "{\n" + " \"homepage\": \"http://www.cnn.com\",\n" + " \"some_directory\": \"/usr/local/\",\n" + " \"tabs\": {\n" + " \"new_windows_in_tabs\": true,\n" + " \"max_tabs\": 20\n" + " }\n" + "}"; + +const char kInvalidJson[] = "!@#$%^&"; + +// Expected output for tests using RunBasicJsonPrefStoreTest(). +const char kWriteGolden[] = + "{\"homepage\":\"http://www.cnn.com\"," + "\"long_int\":{\"pref\":\"214748364842\"}," + "\"some_directory\":\"/usr/sbin/\"," + "\"tabs\":{\"max_tabs\":10,\"new_windows_in_tabs\":false}}"; + +// A PrefFilter that will intercept all calls to FilterOnLoad() and hold on +// to the |prefs| until explicitly asked to release them. +class InterceptingPrefFilter : public PrefFilter { + public: + InterceptingPrefFilter(); + InterceptingPrefFilter(OnWriteCallbackPair callback_pair); + ~InterceptingPrefFilter() override; + + // PrefFilter implementation: + void FilterOnLoad( + const PostFilterOnLoadCallback& post_filter_on_load_callback, + std::unique_ptr<base::DictionaryValue> pref_store_contents) override; + void FilterUpdate(const std::string& path) override {} + OnWriteCallbackPair FilterSerializeData( + base::DictionaryValue* pref_store_contents) override { + return on_write_callback_pair_; + } + void OnStoreDeletionFromDisk() override {} + + bool has_intercepted_prefs() const { return intercepted_prefs_ != nullptr; } + + // Finalize an intercepted read, handing |intercepted_prefs_| back to its + // JsonPrefStore. + void ReleasePrefs(); + + private: + PostFilterOnLoadCallback post_filter_on_load_callback_; + std::unique_ptr<base::DictionaryValue> intercepted_prefs_; + OnWriteCallbackPair on_write_callback_pair_; + + DISALLOW_COPY_AND_ASSIGN(InterceptingPrefFilter); +}; + +InterceptingPrefFilter::InterceptingPrefFilter() {} + +InterceptingPrefFilter::InterceptingPrefFilter( + OnWriteCallbackPair callback_pair) { + on_write_callback_pair_ = callback_pair; +} + +InterceptingPrefFilter::~InterceptingPrefFilter() {} + +void InterceptingPrefFilter::FilterOnLoad( + const PostFilterOnLoadCallback& post_filter_on_load_callback, + std::unique_ptr<base::DictionaryValue> pref_store_contents) { + post_filter_on_load_callback_ = post_filter_on_load_callback; + intercepted_prefs_ = std::move(pref_store_contents); +} + +void InterceptingPrefFilter::ReleasePrefs() { + EXPECT_FALSE(post_filter_on_load_callback_.is_null()); + post_filter_on_load_callback_.Run(std::move(intercepted_prefs_), false); + post_filter_on_load_callback_.Reset(); +} + +class MockPrefStoreObserver : public PrefStore::Observer { + public: + MOCK_METHOD1(OnPrefValueChanged, void (const std::string&)); + MOCK_METHOD1(OnInitializationCompleted, void (bool)); +}; + +class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate { + public: + MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError)); +}; + +enum class CommitPendingWriteMode { + // Basic mode. + WITHOUT_CALLBACK, + // With reply callback. + WITH_CALLBACK, + // With synchronous notify callback (synchronous after the write -- shouldn't + // require pumping messages to observe). + WITH_SYNCHRONOUS_CALLBACK, +}; + +base::test::TaskEnvironment::ThreadPoolExecutionMode GetExecutionMode( + CommitPendingWriteMode commit_mode) { + switch (commit_mode) { + case CommitPendingWriteMode::WITHOUT_CALLBACK: + FALLTHROUGH; + case CommitPendingWriteMode::WITH_CALLBACK: + return base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED; + case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK: + // Synchronous callbacks require async tasks to run on their own. + return base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC; + } +} + +void CommitPendingWrite(JsonPrefStore* pref_store, + CommitPendingWriteMode commit_pending_write_mode, + base::test::TaskEnvironment* task_environment) { + switch (commit_pending_write_mode) { + case CommitPendingWriteMode::WITHOUT_CALLBACK: { + pref_store->CommitPendingWrite(); + task_environment->RunUntilIdle(); + break; + } + case CommitPendingWriteMode::WITH_CALLBACK: { + TestCommitPendingWriteWithCallback(pref_store, task_environment); + break; + } + case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK: { + base::WaitableEvent written; + pref_store->CommitPendingWrite( + base::OnceClosure(), + base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written))); + written.Wait(); + break; + } + } +} + +class JsonPrefStoreTest + : public testing::TestWithParam<CommitPendingWriteMode> { + public: + JsonPrefStoreTest() + : task_environment_(base::test::TaskEnvironment::MainThreadType::DEFAULT, + GetExecutionMode(GetParam())) {} + + protected: + void SetUp() override { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + } + + base::test::TaskEnvironment task_environment_; + + // The path to temporary directory used to contain the test operations. + base::ScopedTempDir temp_dir_; + + DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreTest); +}; + +} // namespace + +// Test fallback behavior for a nonexistent file. +TEST_P(JsonPrefStoreTest, NonExistentFile) { + base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt"); + ASSERT_FALSE(PathExists(bogus_input_file)); + auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file); + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, + pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); +} + +// Test fallback behavior for an invalid file. +TEST_P(JsonPrefStoreTest, InvalidFile) { + base::FilePath invalid_file = temp_dir_.GetPath().AppendASCII("invalid.json"); + ASSERT_LT(0, base::WriteFile(invalid_file, kInvalidJson, + base::size(kInvalidJson) - 1)); + + auto pref_store = base::MakeRefCounted<JsonPrefStore>(invalid_file); + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, + pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); + + // The file should have been moved aside. + EXPECT_FALSE(PathExists(invalid_file)); + base::FilePath moved_aside = temp_dir_.GetPath().AppendASCII("invalid.bad"); + EXPECT_TRUE(PathExists(moved_aside)); + + std::string moved_aside_contents; + ASSERT_TRUE(base::ReadFileToString(moved_aside, &moved_aside_contents)); + EXPECT_EQ(kInvalidJson, moved_aside_contents); +} + +// This function is used to avoid code duplication while testing synchronous +// and asynchronous version of the JsonPrefStore loading. It validates that the +// given output file's contents matches kWriteGolden. +void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store, + const base::FilePath& output_file, + CommitPendingWriteMode commit_pending_write_mode, + base::test::TaskEnvironment* task_environment) { + const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs"; + const char kMaxTabs[] = "tabs.max_tabs"; + const char kLongIntPref[] = "long_int.pref"; + + std::string cnn("http://www.cnn.com"); + + const Value* actual; + EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual)); + std::string string_value; + EXPECT_TRUE(actual->GetAsString(&string_value)); + EXPECT_EQ(cnn, string_value); + + const char kSomeDirectory[] = "some_directory"; + + EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual)); + base::FilePath::StringType path; + EXPECT_TRUE(actual->GetAsString(&path)); + EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path); + base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/")); + + pref_store->SetValue(kSomeDirectory, + std::make_unique<Value>(some_path.value()), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual)); + EXPECT_TRUE(actual->GetAsString(&path)); + EXPECT_EQ(some_path.value(), path); + + // Test reading some other data types from sub-dictionaries. + EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual)); + bool boolean = false; + EXPECT_TRUE(actual->GetAsBoolean(&boolean)); + EXPECT_TRUE(boolean); + + pref_store->SetValue(kNewWindowsInTabs, std::make_unique<Value>(false), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual)); + EXPECT_TRUE(actual->GetAsBoolean(&boolean)); + EXPECT_FALSE(boolean); + + EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual)); + int integer = 0; + EXPECT_TRUE(actual->GetAsInteger(&integer)); + EXPECT_EQ(20, integer); + pref_store->SetValue(kMaxTabs, std::make_unique<Value>(10), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual)); + EXPECT_TRUE(actual->GetAsInteger(&integer)); + EXPECT_EQ(10, integer); + + pref_store->SetValue( + kLongIntPref, + std::make_unique<Value>(base::NumberToString(214748364842LL)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual)); + EXPECT_TRUE(actual->GetAsString(&string_value)); + int64_t value; + base::StringToInt64(string_value, &value); + EXPECT_EQ(214748364842LL, value); + + // Serialize and compare to expected output. + CommitPendingWrite(pref_store, commit_pending_write_mode, task_environment); + + std::string output_contents; + ASSERT_TRUE(base::ReadFileToString(output_file, &output_contents)); + EXPECT_EQ(kWriteGolden, output_contents); + ASSERT_TRUE(base::DeleteFile(output_file, false)); +} + +TEST_P(JsonPrefStoreTest, Basic) { + base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json"); + ASSERT_LT(0, + base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1)); + + // Test that the persistent value can be loaded. + ASSERT_TRUE(PathExists(input_file)); + auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(), + &task_environment_); +} + +TEST_P(JsonPrefStoreTest, BasicAsync) { + base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json"); + ASSERT_LT(0, + base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1)); + + // Test that the persistent value can be loaded. + auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file); + + { + MockPrefStoreObserver mock_observer; + pref_store->AddObserver(&mock_observer); + + MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate; + pref_store->ReadPrefsAsync(mock_error_delegate); + + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); + EXPECT_CALL(*mock_error_delegate, + OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); + task_environment_.RunUntilIdle(); + pref_store->RemoveObserver(&mock_observer); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + } + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(), + &task_environment_); +} + +TEST_P(JsonPrefStoreTest, PreserveEmptyValues) { + FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json"); + + auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file); + + // Set some keys with empty values. + pref_store->SetValue("list", std::make_unique<base::ListValue>(), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + pref_store->SetValue("dict", std::make_unique<base::DictionaryValue>(), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + // Write to file. + CommitPendingWrite(pref_store.get(), GetParam(), &task_environment_); + + // Reload. + pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + ASSERT_FALSE(pref_store->ReadOnly()); + + // Check values. + const Value* result = nullptr; + EXPECT_TRUE(pref_store->GetValue("list", &result)); + EXPECT_TRUE(ListValue().Equals(result)); + EXPECT_TRUE(pref_store->GetValue("dict", &result)); + EXPECT_TRUE(DictionaryValue().Equals(result)); +} + +// This test is just documenting some potentially non-obvious behavior. It +// shouldn't be taken as normative. +TEST_P(JsonPrefStoreTest, RemoveClearsEmptyParent) { + FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json"); + + auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file); + + std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue); + dict->SetString("key", "value"); + pref_store->SetValue("dict", std::move(dict), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + pref_store->RemoveValue("dict.key", + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + const base::Value* retrieved_dict = nullptr; + bool has_dict = pref_store->GetValue("dict", &retrieved_dict); + EXPECT_FALSE(has_dict); +} + +// Tests asynchronous reading of the file when there is no file. +TEST_P(JsonPrefStoreTest, AsyncNonExistingFile) { + base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt"); + ASSERT_FALSE(PathExists(bogus_input_file)); + auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file); + MockPrefStoreObserver mock_observer; + pref_store->AddObserver(&mock_observer); + + MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate; + pref_store->ReadPrefsAsync(mock_error_delegate); + + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); + EXPECT_CALL(*mock_error_delegate, + OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1); + task_environment_.RunUntilIdle(); + pref_store->RemoveObserver(&mock_observer); + + EXPECT_FALSE(pref_store->ReadOnly()); +} + +TEST_P(JsonPrefStoreTest, ReadWithInterceptor) { + base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json"); + ASSERT_LT(0, + base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1)); + + std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter( + new InterceptingPrefFilter()); + InterceptingPrefFilter* raw_intercepting_pref_filter_ = + intercepting_pref_filter.get(); + auto pref_store = base::MakeRefCounted<JsonPrefStore>( + input_file, std::move(intercepting_pref_filter)); + + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE, + pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); + + // The store shouldn't be considered initialized until the interceptor + // returns. + EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_FALSE(pref_store->IsInitializationComplete()); + EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr)); + + raw_intercepting_pref_filter_->ReleasePrefs(); + + EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr)); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(), + &task_environment_); +} + +TEST_P(JsonPrefStoreTest, ReadAsyncWithInterceptor) { + base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json"); + ASSERT_LT(0, + base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1)); + + std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter( + new InterceptingPrefFilter()); + InterceptingPrefFilter* raw_intercepting_pref_filter_ = + intercepting_pref_filter.get(); + auto pref_store = base::MakeRefCounted<JsonPrefStore>( + input_file, std::move(intercepting_pref_filter)); + + MockPrefStoreObserver mock_observer; + pref_store->AddObserver(&mock_observer); + + // Ownership of the |mock_error_delegate| is handed to the |pref_store| below. + MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate; + + { + pref_store->ReadPrefsAsync(mock_error_delegate); + + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0); + // EXPECT_CALL(*mock_error_delegate, + // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); + task_environment_.RunUntilIdle(); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_FALSE(pref_store->IsInitializationComplete()); + EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr)); + } + + { + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); + // EXPECT_CALL(*mock_error_delegate, + // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); + + raw_intercepting_pref_filter_->ReleasePrefs(); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr)); + } + + pref_store->RemoveObserver(&mock_observer); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(), + &task_environment_); +} + +INSTANTIATE_TEST_SUITE_P( + WithoutCallback, + JsonPrefStoreTest, + ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK)); +INSTANTIATE_TEST_SUITE_P( + WithCallback, + JsonPrefStoreTest, + ::testing::Values(CommitPendingWriteMode::WITH_CALLBACK)); +INSTANTIATE_TEST_SUITE_P( + WithSynchronousCallback, + JsonPrefStoreTest, + ::testing::Values(CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK)); + +class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest { + public: + JsonPrefStoreLossyWriteTest() = default; + + protected: + void SetUp() override { + JsonPrefStoreTest::SetUp(); + test_file_ = temp_dir_.GetPath().AppendASCII("test.json"); + } + + scoped_refptr<JsonPrefStore> CreatePrefStore() { + return base::MakeRefCounted<JsonPrefStore>(test_file_); + } + + // Return the ImportantFileWriter for a given JsonPrefStore. + ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) { + return &(pref_store->writer_); + } + + // Get the contents of kTestFile. Pumps the message loop before returning the + // result. + std::string GetTestFileContents() { + task_environment_.RunUntilIdle(); + std::string file_contents; + ReadFileToString(test_file_, &file_contents); + return file_contents; + } + + private: + base::FilePath test_file_; + + DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreLossyWriteTest); +}; + +TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteBasic) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); + + // Set a normal pref and check that it gets scheduled to be written. + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->SetValue("normal", std::make_unique<base::Value>("normal"), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ASSERT_TRUE(file_writer->HasPendingWrite()); + file_writer->DoScheduledWrite(); + ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents()); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Set a lossy pref and check that it is not scheduled to be written. + // SetValue/RemoveValue. + pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // SetValueSilently/RemoveValueSilently. + pref_store->SetValueSilently("lossy", std::make_unique<base::Value>("lossy"), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->RemoveValueSilently("lossy", + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // ReportValueChanged. + pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->ReportValueChanged("lossy", + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Call CommitPendingWrite and check that the lossy pref and the normal pref + // are there with the last values set above. + pref_store->CommitPendingWrite(base::OnceClosure()); + ASSERT_FALSE(file_writer->HasPendingWrite()); + ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", + GetTestFileContents()); +} + +TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); + + // Set a lossy pref and check that it is not scheduled to be written. + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Set a normal pref and check that it is scheduled to be written. + pref_store->SetValue("normal", std::make_unique<base::Value>("normal"), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Call DoScheduledWrite and check both prefs get written. + file_writer->DoScheduledWrite(); + ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", + GetTestFileContents()); + ASSERT_FALSE(file_writer->HasPendingWrite()); +} + +TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); + + // Set a normal pref and check that it is scheduled to be written. + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->SetValue("normal", std::make_unique<base::Value>("normal"), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Set a lossy pref and check that the write is still scheduled. + pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Call DoScheduledWrite and check both prefs get written. + file_writer->DoScheduledWrite(); + ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", + GetTestFileContents()); + ASSERT_FALSE(file_writer->HasPendingWrite()); +} + +TEST_P(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); + + // Set a lossy pref and check that it is not scheduled to be written. + pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Schedule pending lossy writes and check that it is scheduled. + pref_store->SchedulePendingLossyWrites(); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Call CommitPendingWrite and check that the lossy pref is there with the + // last value set above. + pref_store->CommitPendingWrite(base::OnceClosure()); + ASSERT_FALSE(file_writer->HasPendingWrite()); + ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents()); +} + +INSTANTIATE_TEST_SUITE_P( + WithoutCallback, + JsonPrefStoreLossyWriteTest, + ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK)); +INSTANTIATE_TEST_SUITE_P( + WithReply, + JsonPrefStoreLossyWriteTest, + ::testing::Values(CommitPendingWriteMode::WITH_CALLBACK)); +INSTANTIATE_TEST_SUITE_P( + WithNotify, + JsonPrefStoreLossyWriteTest, + ::testing::Values(CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK)); + +class SuccessfulWriteReplyObserver { + public: + SuccessfulWriteReplyObserver() = default; + + // Returns true if a successful write was observed via on_successful_write() + // and resets the observation state to false regardless. + bool GetAndResetObservationState() { + bool was_successful_write_observed = successful_write_reply_observed_; + successful_write_reply_observed_ = false; + return was_successful_write_observed; + } + + // Register OnWrite() to be called on the next write of |json_pref_store|. + void ObserveNextWriteCallback(JsonPrefStore* json_pref_store); + + void OnSuccessfulWrite() { + EXPECT_FALSE(successful_write_reply_observed_); + successful_write_reply_observed_ = true; + } + + private: + bool successful_write_reply_observed_ = false; + + DISALLOW_COPY_AND_ASSIGN(SuccessfulWriteReplyObserver); +}; + +void SuccessfulWriteReplyObserver::ObserveNextWriteCallback( + JsonPrefStore* json_pref_store) { + json_pref_store->RegisterOnNextSuccessfulWriteReply( + base::Bind(&SuccessfulWriteReplyObserver::OnSuccessfulWrite, + base::Unretained(this))); +} + +enum WriteCallbackObservationState { + NOT_CALLED, + CALLED_WITH_ERROR, + CALLED_WITH_SUCCESS, +}; + +class WriteCallbacksObserver { + public: + WriteCallbacksObserver() = default; + + // Register OnWrite() to be called on the next write of |json_pref_store|. + void ObserveNextWriteCallback(JsonPrefStore* json_pref_store); + + // Returns whether OnPreWrite() was called, and resets the observation state + // to false. + bool GetAndResetPreWriteObservationState(); + + // Returns the |WriteCallbackObservationState| which was observed, then resets + // it to |NOT_CALLED|. + WriteCallbackObservationState GetAndResetPostWriteObservationState(); + + JsonPrefStore::OnWriteCallbackPair GetCallbackPair() { + return std::make_pair( + base::Bind(&WriteCallbacksObserver::OnPreWrite, base::Unretained(this)), + base::Bind(&WriteCallbacksObserver::OnPostWrite, + base::Unretained(this))); + } + + void OnPreWrite() { + EXPECT_FALSE(pre_write_called_); + pre_write_called_ = true; + } + + void OnPostWrite(bool success) { + EXPECT_EQ(NOT_CALLED, post_write_observation_state_); + post_write_observation_state_ = + success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR; + } + + private: + bool pre_write_called_ = false; + WriteCallbackObservationState post_write_observation_state_ = NOT_CALLED; + + DISALLOW_COPY_AND_ASSIGN(WriteCallbacksObserver); +}; + +void WriteCallbacksObserver::ObserveNextWriteCallback(JsonPrefStore* writer) { + writer->RegisterOnNextWriteSynchronousCallbacks(GetCallbackPair()); +} + +bool WriteCallbacksObserver::GetAndResetPreWriteObservationState() { + bool observation_state = pre_write_called_; + pre_write_called_ = false; + return observation_state; +} + +WriteCallbackObservationState +WriteCallbacksObserver::GetAndResetPostWriteObservationState() { + WriteCallbackObservationState state = post_write_observation_state_; + pre_write_called_ = false; + post_write_observation_state_ = NOT_CALLED; + return state; +} + +class JsonPrefStoreCallbackTest : public testing::Test { + public: + JsonPrefStoreCallbackTest() = default; + + protected: + void SetUp() override { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + test_file_ = temp_dir_.GetPath().AppendASCII("test.json"); + } + + scoped_refptr<JsonPrefStore> CreatePrefStore() { + return base::MakeRefCounted<JsonPrefStore>(test_file_); + } + + // Return the ImportantFileWriter for a given JsonPrefStore. + ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) { + return &(pref_store->writer_); + } + + void TriggerFakeWriteForCallback(JsonPrefStore* pref_store, bool success) { + JsonPrefStore::PostWriteCallback( + base::Bind(&JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback, + pref_store->AsWeakPtr()), + base::Bind(&WriteCallbacksObserver::OnPostWrite, + base::Unretained(&write_callback_observer_)), + base::SequencedTaskRunnerHandle::Get(), success); + } + + SuccessfulWriteReplyObserver successful_write_reply_observer_; + WriteCallbacksObserver write_callback_observer_; + + protected: + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::MainThreadType::DEFAULT, + base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED}; + + base::ScopedTempDir temp_dir_; + + private: + base::FilePath test_file_; + + DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreCallbackTest); +}; + +TEST_F(JsonPrefStoreCallbackTest, TestSerializeDataCallbacks) { + base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json"); + ASSERT_LT(0, + base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1)); + + std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter( + new InterceptingPrefFilter(write_callback_observer_.GetCallbackPair())); + auto pref_store = base::MakeRefCounted<JsonPrefStore>( + input_file, std::move(intercepting_pref_filter)); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); + + EXPECT_EQ(NOT_CALLED, + write_callback_observer_.GetAndResetPostWriteObservationState()); + pref_store->SetValue("normal", std::make_unique<base::Value>("normal"), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + file_writer->DoScheduledWrite(); + + // The observer should not be invoked right away. + EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState()); + EXPECT_EQ(NOT_CALLED, + write_callback_observer_.GetAndResetPostWriteObservationState()); + + task_environment_.RunUntilIdle(); + + EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState()); + EXPECT_EQ(CALLED_WITH_SUCCESS, + write_callback_observer_.GetAndResetPostWriteObservationState()); +} + +TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacks) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); + + // Test RegisterOnNextWriteSynchronousCallbacks after + // RegisterOnNextSuccessfulWriteReply. + successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get()); + write_callback_observer_.ObserveNextWriteCallback(pref_store.get()); + file_writer->WriteNow(std::make_unique<std::string>("foo")); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState()); + EXPECT_EQ(CALLED_WITH_SUCCESS, + write_callback_observer_.GetAndResetPostWriteObservationState()); + + // Test RegisterOnNextSuccessfulWriteReply after + // RegisterOnNextWriteSynchronousCallbacks. + successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get()); + write_callback_observer_.ObserveNextWriteCallback(pref_store.get()); + file_writer->WriteNow(std::make_unique<std::string>("foo")); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState()); + EXPECT_EQ(CALLED_WITH_SUCCESS, + write_callback_observer_.GetAndResetPostWriteObservationState()); + + // Test RegisterOnNextSuccessfulWriteReply only. + successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get()); + file_writer->WriteNow(std::make_unique<std::string>("foo")); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState()); + EXPECT_EQ(NOT_CALLED, + write_callback_observer_.GetAndResetPostWriteObservationState()); + + // Test RegisterOnNextWriteSynchronousCallbacks only. + write_callback_observer_.ObserveNextWriteCallback(pref_store.get()); + file_writer->WriteNow(std::make_unique<std::string>("foo")); + task_environment_.RunUntilIdle(); + EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState()); + EXPECT_EQ(CALLED_WITH_SUCCESS, + write_callback_observer_.GetAndResetPostWriteObservationState()); +} + +TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksWithFakeFailure) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + + // Confirm that the observers are invoked. + successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get()); + TriggerFakeWriteForCallback(pref_store.get(), true); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_EQ(CALLED_WITH_SUCCESS, + write_callback_observer_.GetAndResetPostWriteObservationState()); + + // Confirm that the observation states were reset. + EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_EQ(NOT_CALLED, + write_callback_observer_.GetAndResetPostWriteObservationState()); + + // Confirm that re-installing the observers works for another write. + successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get()); + TriggerFakeWriteForCallback(pref_store.get(), true); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_EQ(CALLED_WITH_SUCCESS, + write_callback_observer_.GetAndResetPostWriteObservationState()); + + // Confirm that the successful observer is not invoked by an unsuccessful + // write, and that the synchronous observer is invoked. + successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get()); + TriggerFakeWriteForCallback(pref_store.get(), false); + task_environment_.RunUntilIdle(); + EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_EQ(CALLED_WITH_ERROR, + write_callback_observer_.GetAndResetPostWriteObservationState()); + + // Do a real write, and confirm that the successful observer was invoked after + // being set by |PostWriteCallback| by the last TriggerFakeWriteCallback. + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); + file_writer->WriteNow(std::make_unique<std::string>("foo")); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_EQ(NOT_CALLED, + write_callback_observer_.GetAndResetPostWriteObservationState()); +} + +TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksDuringProfileDeath) { + // Create a JsonPrefStore and attach observers to it, then delete it by making + // it go out of scope to simulate profile switch or Chrome shutdown. + { + scoped_refptr<JsonPrefStore> soon_out_of_scope_pref_store = + CreatePrefStore(); + ImportantFileWriter* file_writer = + GetImportantFileWriter(soon_out_of_scope_pref_store.get()); + successful_write_reply_observer_.ObserveNextWriteCallback( + soon_out_of_scope_pref_store.get()); + write_callback_observer_.ObserveNextWriteCallback( + soon_out_of_scope_pref_store.get()); + file_writer->WriteNow(std::make_unique<std::string>("foo")); + } + task_environment_.RunUntilIdle(); + EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState()); + EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState()); + EXPECT_EQ(CALLED_WITH_SUCCESS, + write_callback_observer_.GetAndResetPostWriteObservationState()); +} + +} // namespace base
diff --git a/src/updater_components/prefs/mock_pref_change_callback.cc b/src/updater_components/prefs/mock_pref_change_callback.cc new file mode 100644 index 0000000..7ac2cd7 --- /dev/null +++ b/src/updater_components/prefs/mock_pref_change_callback.cc
@@ -0,0 +1,24 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/mock_pref_change_callback.h" + +#include "base/bind.h" + +MockPrefChangeCallback::MockPrefChangeCallback(PrefService* prefs) + : prefs_(prefs) { +} + +MockPrefChangeCallback::~MockPrefChangeCallback() {} + +PrefChangeRegistrar::NamedChangeCallback MockPrefChangeCallback::GetCallback() { + return base::Bind(&MockPrefChangeCallback::OnPreferenceChanged, + base::Unretained(this)); +} + +void MockPrefChangeCallback::Expect(const std::string& pref_name, + const base::Value* value) { + EXPECT_CALL(*this, OnPreferenceChanged(pref_name)) + .With(PrefValueMatches(prefs_, pref_name, value)); +}
diff --git a/src/updater_components/prefs/mock_pref_change_callback.h b/src/updater_components/prefs/mock_pref_change_callback.h new file mode 100644 index 0000000..9c0aeec --- /dev/null +++ b/src/updater_components/prefs/mock_pref_change_callback.h
@@ -0,0 +1,51 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_ +#define COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_ + +#include <string> + +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::Pointee; +using testing::Property; +using testing::Truly; + +// Matcher that checks whether the current value of the preference named +// |pref_name| in |prefs| matches |value|. If |value| is NULL, the matcher +// checks that the value is not set. +MATCHER_P3(PrefValueMatches, prefs, pref_name, value, "") { + const PrefService::Preference* pref = prefs->FindPreference(pref_name); + if (!pref) + return false; + + const base::Value* actual_value = pref->GetValue(); + if (!actual_value) + return value == NULL; + if (!value) + return actual_value == NULL; + return value->Equals(actual_value); +} + +// A mock for testing preference notifications and easy setup of expectations. +class MockPrefChangeCallback { + public: + explicit MockPrefChangeCallback(PrefService* prefs); + virtual ~MockPrefChangeCallback(); + + PrefChangeRegistrar::NamedChangeCallback GetCallback(); + + MOCK_METHOD1(OnPreferenceChanged, void(const std::string&)); + + void Expect(const std::string& pref_name, + const base::Value* value); + + private: + PrefService* prefs_; +}; + +#endif // COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_
diff --git a/src/updater_components/prefs/overlay_user_pref_store.cc b/src/updater_components/prefs/overlay_user_pref_store.cc new file mode 100644 index 0000000..0baa6c4 --- /dev/null +++ b/src/updater_components/prefs/overlay_user_pref_store.cc
@@ -0,0 +1,247 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/overlay_user_pref_store.h" + +#include <memory> +#include <utility> + +#include "base/memory/ptr_util.h" +#include "base/values.h" +#include "components/prefs/in_memory_pref_store.h" + +// Allows us to monitor two pref stores and tell updates from them apart. It +// essentially mimics a Callback for the Observer interface (e.g. it allows +// binding additional arguments). +class OverlayUserPrefStore::ObserverAdapter : public PrefStore::Observer { + public: + ObserverAdapter(bool ephemeral, OverlayUserPrefStore* parent) + : ephemeral_user_pref_store_(ephemeral), parent_(parent) {} + + // Methods of PrefStore::Observer. + void OnPrefValueChanged(const std::string& key) override { + parent_->OnPrefValueChanged(ephemeral_user_pref_store_, key); + } + void OnInitializationCompleted(bool succeeded) override { + parent_->OnInitializationCompleted(ephemeral_user_pref_store_, succeeded); + } + + private: + // Is the update for the ephemeral? + const bool ephemeral_user_pref_store_; + OverlayUserPrefStore* const parent_; +}; + +OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* persistent) + : OverlayUserPrefStore(new InMemoryPrefStore(), persistent) {} + +OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* ephemeral, + PersistentPrefStore* persistent) + : ephemeral_pref_store_observer_( + std::make_unique<OverlayUserPrefStore::ObserverAdapter>(true, this)), + persistent_pref_store_observer_( + std::make_unique<OverlayUserPrefStore::ObserverAdapter>(false, this)), + ephemeral_user_pref_store_(ephemeral), + persistent_user_pref_store_(persistent) { + DCHECK(ephemeral->IsInitializationComplete()); + ephemeral_user_pref_store_->AddObserver(ephemeral_pref_store_observer_.get()); + persistent_user_pref_store_->AddObserver( + persistent_pref_store_observer_.get()); +} + +bool OverlayUserPrefStore::IsSetInOverlay(const std::string& key) const { + return ephemeral_user_pref_store_->GetValue(key, nullptr); +} + +void OverlayUserPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void OverlayUserPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool OverlayUserPrefStore::HasObservers() const { + return observers_.might_have_observers(); +} + +bool OverlayUserPrefStore::IsInitializationComplete() const { + return persistent_user_pref_store_->IsInitializationComplete() && + ephemeral_user_pref_store_->IsInitializationComplete(); +} + +bool OverlayUserPrefStore::GetValue(const std::string& key, + const base::Value** result) const { + // If the |key| shall NOT be stored in the ephemeral store, there must not + // be an entry. + DCHECK(!ShallBeStoredInPersistent(key) || + !ephemeral_user_pref_store_->GetValue(key, nullptr)); + + if (ephemeral_user_pref_store_->GetValue(key, result)) + return true; + return persistent_user_pref_store_->GetValue(key, result); +} + +std::unique_ptr<base::DictionaryValue> OverlayUserPrefStore::GetValues() const { + auto values = ephemeral_user_pref_store_->GetValues(); + auto persistent_values = persistent_user_pref_store_->GetValues(); + + // Output |values| are read from |ephemeral_user_pref_store_| (in-memory + // store). Then the values of preferences in |persistent_names_set_| are + // overwritten by the content of |persistent_user_pref_store_| (the persistent + // store). + for (const auto& key : persistent_names_set_) { + std::unique_ptr<base::Value> out_value; + persistent_values->Remove(key, &out_value); + if (out_value) { + values->Set(key, std::move(out_value)); + } + } + return values; +} + +bool OverlayUserPrefStore::GetMutableValue(const std::string& key, + base::Value** result) { + if (ShallBeStoredInPersistent(key)) + return persistent_user_pref_store_->GetMutableValue(key, result); + + written_ephemeral_names_.insert(key); + if (ephemeral_user_pref_store_->GetMutableValue(key, result)) + return true; + + // Try to create copy of persistent if the ephemeral does not contain a value. + base::Value* persistent_value = nullptr; + if (!persistent_user_pref_store_->GetMutableValue(key, &persistent_value)) + return false; + + ephemeral_user_pref_store_->SetValue( + key, persistent_value->CreateDeepCopy(), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ephemeral_user_pref_store_->GetMutableValue(key, result); + return true; +} + +void OverlayUserPrefStore::SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + if (ShallBeStoredInPersistent(key)) { + persistent_user_pref_store_->SetValue(key, std::move(value), flags); + return; + } + + // TODO(https://crbug.com/861722): If we always store in in-memory storage + // and conditionally also stored in persistent one, we wouldn't have to do a + // complex merge in GetValues(). + written_ephemeral_names_.insert(key); + ephemeral_user_pref_store_->SetValue(key, std::move(value), flags); +} + +void OverlayUserPrefStore::SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + if (ShallBeStoredInPersistent(key)) { + persistent_user_pref_store_->SetValueSilently(key, std::move(value), flags); + return; + } + + written_ephemeral_names_.insert(key); + ephemeral_user_pref_store_->SetValueSilently(key, std::move(value), flags); +} + +void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32_t flags) { + if (ShallBeStoredInPersistent(key)) { + persistent_user_pref_store_->RemoveValue(key, flags); + return; + } + + written_ephemeral_names_.insert(key); + ephemeral_user_pref_store_->RemoveValue(key, flags); +} + +bool OverlayUserPrefStore::ReadOnly() const { + return false; +} + +PersistentPrefStore::PrefReadError OverlayUserPrefStore::GetReadError() const { + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +PersistentPrefStore::PrefReadError OverlayUserPrefStore::ReadPrefs() { + // We do not read intentionally. + OnInitializationCompleted(/* ephemeral */ false, true); + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +void OverlayUserPrefStore::ReadPrefsAsync( + ReadErrorDelegate* error_delegate_raw) { + std::unique_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw); + // We do not read intentionally. + OnInitializationCompleted(/* ephemeral */ false, true); +} + +void OverlayUserPrefStore::CommitPendingWrite( + base::OnceClosure reply_callback, + base::OnceClosure synchronous_done_callback) { + persistent_user_pref_store_->CommitPendingWrite( + std::move(reply_callback), std::move(synchronous_done_callback)); + // We do not write our content intentionally. +} + +void OverlayUserPrefStore::SchedulePendingLossyWrites() { + persistent_user_pref_store_->SchedulePendingLossyWrites(); +} + +void OverlayUserPrefStore::ReportValueChanged(const std::string& key, + uint32_t flags) { + for (PrefStore::Observer& observer : observers_) + observer.OnPrefValueChanged(key); +} + +void OverlayUserPrefStore::RegisterPersistentPref(const std::string& key) { + DCHECK(!key.empty()) << "Key is empty"; + DCHECK(persistent_names_set_.find(key) == persistent_names_set_.end()) + << "Key already registered: " << key; + persistent_names_set_.insert(key); +} + +void OverlayUserPrefStore::ClearMutableValues() { + for (const auto& key : written_ephemeral_names_) { + ephemeral_user_pref_store_->RemoveValue( + key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } +} + +void OverlayUserPrefStore::OnStoreDeletionFromDisk() { + persistent_user_pref_store_->OnStoreDeletionFromDisk(); +} + +OverlayUserPrefStore::~OverlayUserPrefStore() { + ephemeral_user_pref_store_->RemoveObserver( + ephemeral_pref_store_observer_.get()); + persistent_user_pref_store_->RemoveObserver( + persistent_pref_store_observer_.get()); +} + +void OverlayUserPrefStore::OnPrefValueChanged(bool ephemeral, + const std::string& key) { + if (ephemeral) { + ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS); + } else { + if (!ephemeral_user_pref_store_->GetValue(key, nullptr)) + ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS); + } +} + +void OverlayUserPrefStore::OnInitializationCompleted(bool ephemeral, + bool succeeded) { + if (!IsInitializationComplete()) + return; + for (PrefStore::Observer& observer : observers_) + observer.OnInitializationCompleted(succeeded); +} + +bool OverlayUserPrefStore::ShallBeStoredInPersistent( + const std::string& key) const { + return persistent_names_set_.find(key) != persistent_names_set_.end(); +}
diff --git a/src/updater_components/prefs/overlay_user_pref_store.h b/src/updater_components/prefs/overlay_user_pref_store.h new file mode 100644 index 0000000..9b7c95a --- /dev/null +++ b/src/updater_components/prefs/overlay_user_pref_store.h
@@ -0,0 +1,97 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_ +#define COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_ + +#include <stdint.h> + +#include <map> +#include <string> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/prefs/pref_value_map.h" +#include "components/prefs/prefs_export.h" + +// PersistentPrefStore that directs all write operations into an in-memory +// PrefValueMap. Read operations are first answered by the PrefValueMap. +// If the PrefValueMap does not contain a value for the requested key, +// the look-up is passed on to an underlying PersistentPrefStore +// |persistent_user_pref_store_|. +class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore + : public PersistentPrefStore { + public: + explicit OverlayUserPrefStore(PersistentPrefStore* persistent); + // The |ephemeral| store must already be initialized. + OverlayUserPrefStore(PersistentPrefStore* ephemeral, + PersistentPrefStore* persistent); + + // Returns true if a value has been set for the |key| in this + // OverlayUserPrefStore, i.e. if it potentially overrides a value + // from the |persistent_user_pref_store_|. + virtual bool IsSetInOverlay(const std::string& key) const; + + // Methods of PrefStore. + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + bool IsInitializationComplete() const override; + bool GetValue(const std::string& key, + const base::Value** result) const override; + std::unique_ptr<base::DictionaryValue> GetValues() const override; + + // Methods of PersistentPrefStore. + bool GetMutableValue(const std::string& key, base::Value** result) override; + void SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + void SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + void RemoveValue(const std::string& key, uint32_t flags) override; + bool ReadOnly() const override; + PrefReadError GetReadError() const override; + PrefReadError ReadPrefs() override; + void ReadPrefsAsync(ReadErrorDelegate* delegate) override; + void CommitPendingWrite(base::OnceClosure reply_callback, + base::OnceClosure synchronous_done_callback) override; + void SchedulePendingLossyWrites() override; + void ReportValueChanged(const std::string& key, uint32_t flags) override; + + // Registers preferences that should be stored in the persistent preferences + // (|persistent_user_pref_store_|). + void RegisterPersistentPref(const std::string& key); + + void ClearMutableValues() override; + void OnStoreDeletionFromDisk() override; + + protected: + ~OverlayUserPrefStore() override; + + private: + typedef std::set<std::string> NamesSet; + class ObserverAdapter; + + void OnPrefValueChanged(bool ephemeral, const std::string& key); + void OnInitializationCompleted(bool ephemeral, bool succeeded); + + // Returns true if |key| corresponds to a preference that shall be stored in + // persistent PrefStore. + bool ShallBeStoredInPersistent(const std::string& key) const; + + base::ObserverList<PrefStore::Observer, true>::Unchecked observers_; + std::unique_ptr<ObserverAdapter> ephemeral_pref_store_observer_; + std::unique_ptr<ObserverAdapter> persistent_pref_store_observer_; + scoped_refptr<PersistentPrefStore> ephemeral_user_pref_store_; + scoped_refptr<PersistentPrefStore> persistent_user_pref_store_; + NamesSet persistent_names_set_; + NamesSet written_ephemeral_names_; + + DISALLOW_COPY_AND_ASSIGN(OverlayUserPrefStore); +}; + +#endif // COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_
diff --git a/src/updater_components/prefs/overlay_user_pref_store_unittest.cc b/src/updater_components/prefs/overlay_user_pref_store_unittest.cc new file mode 100644 index 0000000..1c0c855 --- /dev/null +++ b/src/updater_components/prefs/overlay_user_pref_store_unittest.cc
@@ -0,0 +1,282 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/overlay_user_pref_store.h" + +#include <memory> + +#include "base/test/task_environment.h" +#include "base/values.h" +#include "components/prefs/persistent_pref_store_unittest.h" +#include "components/prefs/pref_store_observer_mock.h" +#include "components/prefs/testing_pref_store.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::Mock; +using ::testing::StrEq; + +namespace base { +namespace { + +const char kBrowserWindowPlacement[] = "browser.window_placement"; +const char kShowBookmarkBar[] = "bookmark_bar.show_on_all_tabs"; +const char kSharedKey[] = "sync_promo.show_on_first_run_allowed"; + +const char* const regular_key = kBrowserWindowPlacement; +const char* const persistent_key = kShowBookmarkBar; +const char* const shared_key = kSharedKey; + +} // namespace + +class OverlayUserPrefStoreTest : public testing::Test { + protected: + OverlayUserPrefStoreTest() + : underlay_(new TestingPrefStore()), + overlay_(new OverlayUserPrefStore(underlay_.get())) { + overlay_->RegisterPersistentPref(persistent_key); + overlay_->RegisterPersistentPref(shared_key); + } + + ~OverlayUserPrefStoreTest() override {} + + base::test::TaskEnvironment task_environment_; + scoped_refptr<TestingPrefStore> underlay_; + scoped_refptr<OverlayUserPrefStore> overlay_; +}; + +TEST_F(OverlayUserPrefStoreTest, Observer) { + PrefStoreObserverMock obs; + overlay_->AddObserver(&obs); + + // Check that underlay first value is reported. + underlay_->SetValue(regular_key, std::make_unique<Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(regular_key); + + // Check that underlay overwriting is reported. + underlay_->SetValue(regular_key, std::make_unique<Value>(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(regular_key); + + // Check that overwriting change in overlay is reported. + overlay_->SetValue(regular_key, std::make_unique<Value>(44), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(regular_key); + + // Check that hidden underlay change is not reported. + underlay_->SetValue(regular_key, std::make_unique<Value>(45), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); + + // Check that overlay remove is reported. + overlay_->RemoveValue(regular_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(regular_key); + + // Check that underlay remove is reported. + underlay_->RemoveValue(regular_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(regular_key); + + // Check respecting of silence. + overlay_->SetValueSilently(regular_key, std::make_unique<Value>(46), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); + + overlay_->RemoveObserver(&obs); + + // Check successful unsubscription. + underlay_->SetValue(regular_key, std::make_unique<Value>(47), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(regular_key, std::make_unique<Value>(48), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); +} + +TEST_F(OverlayUserPrefStoreTest, GetAndSet) { + const Value* value = nullptr; + EXPECT_FALSE(overlay_->GetValue(regular_key, &value)); + EXPECT_FALSE(underlay_->GetValue(regular_key, &value)); + + underlay_->SetValue(regular_key, std::make_unique<Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + // Value shines through: + EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(42).Equals(value)); + + EXPECT_TRUE(underlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(42).Equals(value)); + + overlay_->SetValue(regular_key, std::make_unique<Value>(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(43).Equals(value)); + + EXPECT_TRUE(underlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(42).Equals(value)); + + overlay_->RemoveValue(regular_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + // Value shines through: + EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(42).Equals(value)); + + EXPECT_TRUE(underlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(42).Equals(value)); +} + +// Check that GetMutableValue does not return the dictionary of the underlay. +TEST_F(OverlayUserPrefStoreTest, ModifyDictionaries) { + underlay_->SetValue(regular_key, std::make_unique<DictionaryValue>(), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + Value* modify = nullptr; + EXPECT_TRUE(overlay_->GetMutableValue(regular_key, &modify)); + ASSERT_TRUE(modify); + ASSERT_TRUE(modify->is_dict()); + static_cast<DictionaryValue*>(modify)->SetInteger(regular_key, 42); + + Value* original_in_underlay = nullptr; + EXPECT_TRUE(underlay_->GetMutableValue(regular_key, &original_in_underlay)); + ASSERT_TRUE(original_in_underlay); + ASSERT_TRUE(original_in_underlay->is_dict()); + EXPECT_TRUE(static_cast<DictionaryValue*>(original_in_underlay)->empty()); + + Value* modified = nullptr; + EXPECT_TRUE(overlay_->GetMutableValue(regular_key, &modified)); + ASSERT_TRUE(modified); + ASSERT_TRUE(modified->is_dict()); + EXPECT_EQ(*modify, *modified); +} + +// Here we consider a global preference that is not overlayed. +TEST_F(OverlayUserPrefStoreTest, GlobalPref) { + PrefStoreObserverMock obs; + overlay_->AddObserver(&obs); + + const Value* value = nullptr; + + // Check that underlay first value is reported. + underlay_->SetValue(persistent_key, std::make_unique<Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(persistent_key); + + // Check that underlay overwriting is reported. + underlay_->SetValue(persistent_key, std::make_unique<Value>(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(persistent_key); + + // Check that we get this value from the overlay + EXPECT_TRUE(overlay_->GetValue(persistent_key, &value)); + EXPECT_TRUE(base::Value(43).Equals(value)); + + // Check that overwriting change in overlay is reported. + overlay_->SetValue(persistent_key, std::make_unique<Value>(44), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(persistent_key); + + // Check that we get this value from the overlay and the underlay. + EXPECT_TRUE(overlay_->GetValue(persistent_key, &value)); + EXPECT_TRUE(base::Value(44).Equals(value)); + EXPECT_TRUE(underlay_->GetValue(persistent_key, &value)); + EXPECT_TRUE(base::Value(44).Equals(value)); + + // Check that overlay remove is reported. + overlay_->RemoveValue(persistent_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(persistent_key); + + // Check that value was removed from overlay and underlay + EXPECT_FALSE(overlay_->GetValue(persistent_key, &value)); + EXPECT_FALSE(underlay_->GetValue(persistent_key, &value)); + + // Check respecting of silence. + overlay_->SetValueSilently(persistent_key, std::make_unique<Value>(46), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); + + overlay_->RemoveObserver(&obs); + + // Check successful unsubscription. + underlay_->SetValue(persistent_key, std::make_unique<Value>(47), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(persistent_key, std::make_unique<Value>(48), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); +} + +// Check that mutable values are removed correctly. +TEST_F(OverlayUserPrefStoreTest, ClearMutableValues) { + // Set in overlay and underlay the same preference. + underlay_->SetValue(regular_key, std::make_unique<Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(regular_key, std::make_unique<Value>(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + const Value* value = nullptr; + // Check that an overlay preference is returned. + EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(43).Equals(value)); + overlay_->ClearMutableValues(); + + // Check that an underlay preference is returned. + EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(42).Equals(value)); +} + +// Check that mutable values are removed correctly when using a silent set. +TEST_F(OverlayUserPrefStoreTest, ClearMutableValues_Silently) { + // Set in overlay and underlay the same preference. + underlay_->SetValueSilently(regular_key, std::make_unique<Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValueSilently(regular_key, std::make_unique<Value>(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + const Value* value = nullptr; + // Check that an overlay preference is returned. + EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(43).Equals(value)); + overlay_->ClearMutableValues(); + + // Check that an underlay preference is returned. + EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::Value(42).Equals(value)); +} + +TEST_F(OverlayUserPrefStoreTest, GetValues) { + // To check merge behavior, create underlay and overlay so each has a key the + // other doesn't have and they have one key in common. + underlay_->SetValue(persistent_key, std::make_unique<Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(regular_key, std::make_unique<Value>(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + underlay_->SetValue(shared_key, std::make_unique<Value>(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(shared_key, std::make_unique<Value>(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + auto values = overlay_->GetValues(); + const Value* value = nullptr; + // Check that an overlay preference is returned. + ASSERT_TRUE(values->Get(persistent_key, &value)); + EXPECT_TRUE(base::Value(42).Equals(value)); + + // Check that an underlay preference is returned. + ASSERT_TRUE(values->Get(regular_key, &value)); + EXPECT_TRUE(base::Value(43).Equals(value)); + + // Check that the overlay is preferred. + ASSERT_TRUE(values->Get(shared_key, &value)); + EXPECT_TRUE(base::Value(43).Equals(value)); +} + +TEST_F(OverlayUserPrefStoreTest, CommitPendingWriteWithCallback) { + TestCommitPendingWriteWithCallback(overlay_.get(), &task_environment_); +} + +} // namespace base
diff --git a/src/updater_components/prefs/persistent_pref_store.cc b/src/updater_components/prefs/persistent_pref_store.cc new file mode 100644 index 0000000..9086c1e --- /dev/null +++ b/src/updater_components/prefs/persistent_pref_store.cc
@@ -0,0 +1,31 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/persistent_pref_store.h" + +#include <utility> + +#include "base/threading/sequenced_task_runner_handle.h" + +void PersistentPrefStore::CommitPendingWrite( + base::OnceClosure reply_callback, + base::OnceClosure synchronous_done_callback) { + // Default behavior for PersistentPrefStore implementation that don't issue + // disk operations: schedule the callback immediately. + // |synchronous_done_callback| is allowed to be invoked synchronously (and + // must be here since we have no other way to post it which isn't the current + // sequence). + + if (synchronous_done_callback) + std::move(synchronous_done_callback).Run(); + + if (reply_callback) { + base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, + std::move(reply_callback)); + } +} + +bool PersistentPrefStore::IsInMemoryPrefStore() const { + return false; +}
diff --git a/src/updater_components/prefs/persistent_pref_store.h b/src/updater_components/prefs/persistent_pref_store.h new file mode 100644 index 0000000..967ccda --- /dev/null +++ b/src/updater_components/prefs/persistent_pref_store.h
@@ -0,0 +1,95 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_ +#define COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_ + +#include <string> + +#include "base/callback.h" +#include "components/prefs/prefs_export.h" +#include "components/prefs/writeable_pref_store.h" + +// This interface is complementary to the PrefStore interface, declaring +// additional functionality that adds support for setting values and persisting +// the data to some backing store. +class COMPONENTS_PREFS_EXPORT PersistentPrefStore : public WriteablePrefStore { + public: + // Unique integer code for each type of error so we can report them + // distinctly in a histogram. + // NOTE: Don't change the explicit values of the enums as it will change the + // server's meaning of the histogram. + enum PrefReadError { + PREF_READ_ERROR_NONE = 0, + PREF_READ_ERROR_JSON_PARSE = 1, + PREF_READ_ERROR_JSON_TYPE = 2, + PREF_READ_ERROR_ACCESS_DENIED = 3, + PREF_READ_ERROR_FILE_OTHER = 4, + PREF_READ_ERROR_FILE_LOCKED = 5, + PREF_READ_ERROR_NO_FILE = 6, + PREF_READ_ERROR_JSON_REPEAT = 7, + // PREF_READ_ERROR_OTHER = 8, // Deprecated. + PREF_READ_ERROR_FILE_NOT_SPECIFIED = 9, + // Indicates that ReadPrefs() couldn't complete synchronously and is waiting + // for an asynchronous task to complete first. + PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE = 10, + PREF_READ_ERROR_MAX_ENUM + }; + + class ReadErrorDelegate { + public: + virtual ~ReadErrorDelegate() {} + + virtual void OnError(PrefReadError error) = 0; + }; + + // Whether the store is in a pseudo-read-only mode where changes are not + // actually persisted to disk. This happens in some cases when there are + // read errors during startup. + virtual bool ReadOnly() const = 0; + + // Gets the read error. Only valid if IsInitializationComplete() returns true. + virtual PrefReadError GetReadError() const = 0; + + // Reads the preferences from disk. Notifies observers via + // "PrefStore::OnInitializationCompleted" when done. + virtual PrefReadError ReadPrefs() = 0; + + // Reads the preferences from disk asynchronously. Notifies observers via + // "PrefStore::OnInitializationCompleted" when done. Also it fires + // |error_delegate| if it is not NULL and reading error has occurred. + // Owns |error_delegate|. + virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) = 0; + + // Lands pending writes to disk. |reply_callback| will be posted to the + // current sequence when changes have been written. + // |synchronous_done_callback| on the other hand will be invoked right away + // wherever the writes complete (could even be invoked synchronously if no + // writes need to occur); this is useful when the current thread cannot pump + // messages to observe the reply (e.g. nested loops banned on main thread + // during shutdown). |synchronous_done_callback| must be thread-safe. + virtual void CommitPendingWrite( + base::OnceClosure reply_callback = base::OnceClosure(), + base::OnceClosure synchronous_done_callback = base::OnceClosure()); + + // Schedule a write if there is any lossy data pending. Unlike + // CommitPendingWrite() this does not immediately sync to disk, instead it + // triggers an eventual write if there is lossy data pending and if there + // isn't one scheduled already. + virtual void SchedulePendingLossyWrites() = 0; + + // It should be called only for Incognito pref store. + virtual void ClearMutableValues() = 0; + + // Cleans preference data that may have been saved outside of the store. + virtual void OnStoreDeletionFromDisk() = 0; + + // TODO(crbug.com/942491) Remove this after fixing the bug. + virtual bool IsInMemoryPrefStore() const; + + protected: + ~PersistentPrefStore() override {} +}; + +#endif // COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_
diff --git a/src/updater_components/prefs/persistent_pref_store_unittest.cc b/src/updater_components/prefs/persistent_pref_store_unittest.cc new file mode 100644 index 0000000..9ea3f98 --- /dev/null +++ b/src/updater_components/prefs/persistent_pref_store_unittest.cc
@@ -0,0 +1,26 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/persistent_pref_store.h" + +#include "base/bind.h" +#include "base/run_loop.h" +#include "base/sequence_checker_impl.h" +#include "base/test/task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +void TestCommitPendingWriteWithCallback( + PersistentPrefStore* store, + base::test::TaskEnvironment* task_environment) { + base::RunLoop run_loop; + base::SequenceCheckerImpl sequence_checker; + store->CommitPendingWrite(base::BindOnce( + [](base::SequenceCheckerImpl* sequence_checker, base::RunLoop* run_loop) { + EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); + run_loop->Quit(); + }, + base::Unretained(&sequence_checker), base::Unretained(&run_loop))); + task_environment->RunUntilIdle(); + run_loop.Run(); +}
diff --git a/src/updater_components/prefs/persistent_pref_store_unittest.h b/src/updater_components/prefs/persistent_pref_store_unittest.h new file mode 100644 index 0000000..121f942 --- /dev/null +++ b/src/updater_components/prefs/persistent_pref_store_unittest.h
@@ -0,0 +1,24 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PERSISTENT_PREF_STORE_UNITTEST_H_ +#define COMPONENTS_PREFS_PERSISTENT_PREF_STORE_UNITTEST_H_ + +namespace base { +namespace test { +class TaskEnvironment; +} +} // namespace base + +class PersistentPrefStore; + +// Calls CommitPendingWrite() on |store| with a callback. Verifies that the +// callback runs on the appropriate sequence. |task_environment| is the +// test's TaskEnvironment. This function is meant to be reused in the +// tests of various PersistentPrefStore implementations. +void TestCommitPendingWriteWithCallback( + PersistentPrefStore* store, + base::test::TaskEnvironment* task_environment); + +#endif // COMPONENTS_PREFS_PERSISTENT_PREF_STORE_UNITTEST_H_
diff --git a/src/updater_components/prefs/pref_change_registrar.cc b/src/updater_components/prefs/pref_change_registrar.cc new file mode 100644 index 0000000..da7de46 --- /dev/null +++ b/src/updater_components/prefs/pref_change_registrar.cc
@@ -0,0 +1,95 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_change_registrar.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "components/prefs/pref_service.h" + +PrefChangeRegistrar::PrefChangeRegistrar() : service_(nullptr) {} + +PrefChangeRegistrar::~PrefChangeRegistrar() { + // If you see an invalid memory access in this destructor, this + // PrefChangeRegistrar might be subscribed to an OffTheRecordProfileImpl that + // has been destroyed. This should not happen any more but be warned. + // Feel free to contact battre@chromium.org in case this happens. + RemoveAll(); +} + +void PrefChangeRegistrar::Init(PrefService* service) { + DCHECK(IsEmpty() || service_ == service); + service_ = service; +} + +void PrefChangeRegistrar::Add(const std::string& path, + const base::Closure& obs) { + Add(path, base::Bind(&PrefChangeRegistrar::InvokeUnnamedCallback, obs)); +} + +void PrefChangeRegistrar::Add(const std::string& path, + const NamedChangeCallback& obs) { + if (!service_) { + NOTREACHED(); + return; + } + DCHECK(!IsObserved(path)) << "Already had pref, \"" << path + << "\", registered."; + + service_->AddPrefObserver(path, this); + observers_[path] = obs; +} + +void PrefChangeRegistrar::Remove(const std::string& path) { + DCHECK(IsObserved(path)); + + observers_.erase(path); + service_->RemovePrefObserver(path, this); +} + +void PrefChangeRegistrar::RemoveAll() { + for (ObserverMap::const_iterator it = observers_.begin(); + it != observers_.end(); ++it) { + service_->RemovePrefObserver(it->first, this); + } + + observers_.clear(); +} + +bool PrefChangeRegistrar::IsEmpty() const { + return observers_.empty(); +} + +bool PrefChangeRegistrar::IsObserved(const std::string& pref) { + return observers_.find(pref) != observers_.end(); +} + +bool PrefChangeRegistrar::IsManaged() { + for (ObserverMap::const_iterator it = observers_.begin(); + it != observers_.end(); ++it) { + const PrefService::Preference* pref = service_->FindPreference(it->first); + if (pref && pref->IsManaged()) + return true; + } + return false; +} + +void PrefChangeRegistrar::OnPreferenceChanged(PrefService* service, + const std::string& pref) { + if (IsObserved(pref)) + observers_[pref].Run(pref); +} + +void PrefChangeRegistrar::InvokeUnnamedCallback(const base::Closure& callback, + const std::string& pref_name) { + callback.Run(); +} + +PrefService* PrefChangeRegistrar::prefs() { + return service_; +} + +const PrefService* PrefChangeRegistrar::prefs() const { + return service_; +}
diff --git a/src/updater_components/prefs/pref_change_registrar.h b/src/updater_components/prefs/pref_change_registrar.h new file mode 100644 index 0000000..8aba1b3 --- /dev/null +++ b/src/updater_components/prefs/pref_change_registrar.h
@@ -0,0 +1,81 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_ +#define COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_ + +#include <map> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "components/prefs/pref_observer.h" +#include "components/prefs/prefs_export.h" + +class PrefService; + +// Automatically manages the registration of one or more pref change observers +// with a PrefStore. Functions much like NotificationRegistrar, but specifically +// manages observers of preference changes. When the Registrar is destroyed, +// all registered observers are automatically unregistered with the PrefStore. +class COMPONENTS_PREFS_EXPORT PrefChangeRegistrar final : public PrefObserver { + public: + // You can register this type of callback if you need to know the + // path of the preference that is changing. + using NamedChangeCallback = base::RepeatingCallback<void(const std::string&)>; + + PrefChangeRegistrar(); + ~PrefChangeRegistrar(); + + // Must be called before adding or removing observers. Can be called more + // than once as long as the value of |service| doesn't change. + void Init(PrefService* service); + + // Adds a pref observer for the specified pref |path| and |obs| observer + // object. All registered observers will be automatically unregistered + // when the registrar's destructor is called. + // + // The second version binds a callback that will receive the path of + // the preference that is changing as its parameter. + // + // Only one observer may be registered per path. + void Add(const std::string& path, const base::Closure& obs); + void Add(const std::string& path, const NamedChangeCallback& obs); + + // Removes the pref observer registered for |path|. + void Remove(const std::string& path); + + // Removes all observers that have been previously added with a call to Add. + void RemoveAll(); + + // Returns true if no pref observers are registered. + bool IsEmpty() const; + + // Check whether |pref| is in the set of preferences being observed. + bool IsObserved(const std::string& pref); + + // Check whether any of the observed preferences has the managed bit set. + bool IsManaged(); + + // Return the PrefService for this registrar. + PrefService* prefs(); + const PrefService* prefs() const; + + private: + // PrefObserver: + void OnPreferenceChanged(PrefService* service, + const std::string& pref_name) override; + + static void InvokeUnnamedCallback(const base::Closure& callback, + const std::string& pref_name); + + using ObserverMap = std::map<std::string, NamedChangeCallback>; + + ObserverMap observers_; + PrefService* service_; + + DISALLOW_COPY_AND_ASSIGN(PrefChangeRegistrar); +}; + +#endif // COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_
diff --git a/src/updater_components/prefs/pref_change_registrar_unittest.cc b/src/updater_components/prefs/pref_change_registrar_unittest.cc new file mode 100644 index 0000000..b02b13a --- /dev/null +++ b/src/updater_components/prefs/pref_change_registrar_unittest.cc
@@ -0,0 +1,201 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_change_registrar.h" + +#include <memory> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "components/prefs/pref_observer.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Mock; +using testing::Eq; + +namespace base { +namespace { + +const char kHomePage[] = "homepage"; +const char kHomePageIsNewTabPage[] = "homepage_is_newtabpage"; +const char kApplicationLocale[] = "intl.app_locale"; + +// A mock provider that allows us to capture pref observer changes. +class MockPrefService : public TestingPrefServiceSimple { + public: + MockPrefService() {} + ~MockPrefService() override {} + + MOCK_METHOD2(AddPrefObserver, void(const std::string&, PrefObserver*)); + MOCK_METHOD2(RemovePrefObserver, void(const std::string&, PrefObserver*)); +}; + +} // namespace + +class PrefChangeRegistrarTest : public testing::Test { + public: + PrefChangeRegistrarTest() {} + ~PrefChangeRegistrarTest() override {} + + protected: + void SetUp() override; + + base::Closure observer() const { return base::DoNothing(); } + + MockPrefService* service() const { return service_.get(); } + + private: + std::unique_ptr<MockPrefService> service_; +}; + +void PrefChangeRegistrarTest::SetUp() { + service_.reset(new MockPrefService()); +} + +TEST_F(PrefChangeRegistrarTest, AddAndRemove) { + PrefChangeRegistrar registrar; + registrar.Init(service()); + + // Test adding. + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.1")), ®istrar)); + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.2")), ®istrar)); + registrar.Add("test.pref.1", observer()); + registrar.Add("test.pref.2", observer()); + EXPECT_FALSE(registrar.IsEmpty()); + + // Test removing. + Mock::VerifyAndClearExpectations(service()); + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.1")), ®istrar)); + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.2")), ®istrar)); + registrar.Remove("test.pref.1"); + registrar.Remove("test.pref.2"); + EXPECT_TRUE(registrar.IsEmpty()); + + // Explicitly check the expectations now to make sure that the Removes + // worked (rather than the registrar destructor doing the work). + Mock::VerifyAndClearExpectations(service()); +} + +TEST_F(PrefChangeRegistrarTest, AutoRemove) { + PrefChangeRegistrar registrar; + registrar.Init(service()); + + // Setup of auto-remove. + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.1")), ®istrar)); + registrar.Add("test.pref.1", observer()); + Mock::VerifyAndClearExpectations(service()); + EXPECT_FALSE(registrar.IsEmpty()); + + // Test auto-removing. + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.1")), ®istrar)); +} + +TEST_F(PrefChangeRegistrarTest, RemoveAll) { + PrefChangeRegistrar registrar; + registrar.Init(service()); + + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.1")), ®istrar)); + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.2")), ®istrar)); + registrar.Add("test.pref.1", observer()); + registrar.Add("test.pref.2", observer()); + Mock::VerifyAndClearExpectations(service()); + + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.1")), ®istrar)); + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.2")), ®istrar)); + registrar.RemoveAll(); + EXPECT_TRUE(registrar.IsEmpty()); + + // Explicitly check the expectations now to make sure that the RemoveAll + // worked (rather than the registrar destructor doing the work). + Mock::VerifyAndClearExpectations(service()); +} + +class ObserveSetOfPreferencesTest : public testing::Test { + public: + void SetUp() override { + pref_service_.reset(new TestingPrefServiceSimple); + PrefRegistrySimple* registry = pref_service_->registry(); + registry->RegisterStringPref(kHomePage, "http://google.com"); + registry->RegisterBooleanPref(kHomePageIsNewTabPage, false); + registry->RegisterStringPref(kApplicationLocale, std::string()); + } + + PrefChangeRegistrar* CreatePrefChangeRegistrar() { + PrefChangeRegistrar* pref_set = new PrefChangeRegistrar(); + base::Closure callback = base::DoNothing(); + pref_set->Init(pref_service_.get()); + pref_set->Add(kHomePage, callback); + pref_set->Add(kHomePageIsNewTabPage, callback); + return pref_set; + } + + MOCK_METHOD1(OnPreferenceChanged, void(const std::string&)); + + std::unique_ptr<TestingPrefServiceSimple> pref_service_; +}; + +TEST_F(ObserveSetOfPreferencesTest, IsObserved) { + std::unique_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar()); + EXPECT_TRUE(pref_set->IsObserved(kHomePage)); + EXPECT_TRUE(pref_set->IsObserved(kHomePageIsNewTabPage)); + EXPECT_FALSE(pref_set->IsObserved(kApplicationLocale)); +} + +TEST_F(ObserveSetOfPreferencesTest, IsManaged) { + std::unique_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar()); + EXPECT_FALSE(pref_set->IsManaged()); + pref_service_->SetManagedPref(kHomePage, + std::make_unique<Value>("http://crbug.com")); + EXPECT_TRUE(pref_set->IsManaged()); + pref_service_->SetManagedPref(kHomePageIsNewTabPage, + std::make_unique<Value>(true)); + EXPECT_TRUE(pref_set->IsManaged()); + pref_service_->RemoveManagedPref(kHomePage); + EXPECT_TRUE(pref_set->IsManaged()); + pref_service_->RemoveManagedPref(kHomePageIsNewTabPage); + EXPECT_FALSE(pref_set->IsManaged()); +} + +TEST_F(ObserveSetOfPreferencesTest, Observe) { + using testing::_; + using testing::Mock; + + PrefChangeRegistrar pref_set; + PrefChangeRegistrar::NamedChangeCallback callback = base::Bind( + &ObserveSetOfPreferencesTest::OnPreferenceChanged, + base::Unretained(this)); + pref_set.Init(pref_service_.get()); + pref_set.Add(kHomePage, callback); + pref_set.Add(kHomePageIsNewTabPage, callback); + + EXPECT_CALL(*this, OnPreferenceChanged(kHomePage)); + pref_service_->SetUserPref(kHomePage, + std::make_unique<Value>("http://crbug.com")); + Mock::VerifyAndClearExpectations(this); + + EXPECT_CALL(*this, OnPreferenceChanged(kHomePageIsNewTabPage)); + pref_service_->SetUserPref(kHomePageIsNewTabPage, + std::make_unique<Value>(true)); + Mock::VerifyAndClearExpectations(this); + + EXPECT_CALL(*this, OnPreferenceChanged(_)).Times(0); + pref_service_->SetUserPref(kApplicationLocale, + std::make_unique<Value>("en_US.utf8")); + Mock::VerifyAndClearExpectations(this); +} + +} // namespace base
diff --git a/src/updater_components/prefs/pref_filter.h b/src/updater_components/prefs/pref_filter.h new file mode 100644 index 0000000..3d52136 --- /dev/null +++ b/src/updater_components/prefs/pref_filter.h
@@ -0,0 +1,66 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_FILTER_H_ +#define COMPONENTS_PREFS_PREF_FILTER_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "base/callback_forward.h" +#include "components/prefs/prefs_export.h" + +namespace base { +class DictionaryValue; +} // namespace base + +// Filters preferences as they are loaded from disk or updated at runtime. +// Currently supported only by JsonPrefStore. +class COMPONENTS_PREFS_EXPORT PrefFilter { + public: + // A pair of pre-write and post-write callbacks. + using OnWriteCallbackPair = + std::pair<base::Closure, base::Callback<void(bool success)>>; + + // A callback to be invoked when |prefs| have been read (and possibly + // pre-modified) and are now ready to be handed back to this callback's + // builder. |schedule_write| indicates whether a write should be immediately + // scheduled (typically because the |prefs| were pre-modified). + using PostFilterOnLoadCallback = + base::Callback<void(std::unique_ptr<base::DictionaryValue> prefs, + bool schedule_write)>; + + virtual ~PrefFilter() {} + + // This method is given ownership of the |pref_store_contents| read from disk + // before the underlying PersistentPrefStore gets to use them. It must hand + // them back via |post_filter_on_load_callback|, but may modify them first. + // Note: This method is asynchronous, which may make calls like + // PersistentPrefStore::ReadPrefs() asynchronous. The owner of filtered + // PersistentPrefStores should handle this to make the reads look synchronous + // to external users (see SegregatedPrefStore::ReadPrefs() for an example). + virtual void FilterOnLoad( + const PostFilterOnLoadCallback& post_filter_on_load_callback, + std::unique_ptr<base::DictionaryValue> pref_store_contents) = 0; + + // Receives notification when a pref store value is changed, before Observers + // are notified. + virtual void FilterUpdate(const std::string& path) = 0; + + // Receives notification when the pref store is about to serialize data + // contained in |pref_store_contents| to a string. Modifications to + // |pref_store_contents| will be persisted to disk and also affect the + // in-memory state. + // If the returned callbacks are non-null, they will be registered to be + // invoked synchronously after the next write (from the I/O TaskRunner so they + // must not be bound to thread-unsafe member state). + virtual OnWriteCallbackPair FilterSerializeData( + base::DictionaryValue* pref_store_contents) = 0; + + // Cleans preference data that may have been saved outside of the store. + virtual void OnStoreDeletionFromDisk() = 0; +}; + +#endif // COMPONENTS_PREFS_PREF_FILTER_H_
diff --git a/src/updater_components/prefs/pref_member.cc b/src/updater_components/prefs/pref_member.cc new file mode 100644 index 0000000..a8fa12c --- /dev/null +++ b/src/updater_components/prefs/pref_member.cc
@@ -0,0 +1,218 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_member.h" + +#include <utility> + +#include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/value_conversions.h" +#include "components/prefs/pref_service.h" + +using base::SequencedTaskRunner; + +namespace subtle { + +PrefMemberBase::PrefMemberBase() : prefs_(nullptr), setting_value_(false) {} + +PrefMemberBase::~PrefMemberBase() { + Destroy(); +} + +void PrefMemberBase::Init(const std::string& pref_name, + PrefService* prefs, + const NamedChangeCallback& observer) { + observer_ = observer; + Init(pref_name, prefs); +} + +void PrefMemberBase::Init(const std::string& pref_name, PrefService* prefs) { + DCHECK(prefs); + DCHECK(pref_name_.empty()); // Check that Init is only called once. + prefs_ = prefs; + pref_name_ = pref_name; + // Check that the preference is registered. + DCHECK(prefs_->FindPreference(pref_name_)) << pref_name << " not registered."; + + // Add ourselves as a pref observer so we can keep our local value in sync. + prefs_->AddPrefObserver(pref_name, this); +} + +void PrefMemberBase::Destroy() { + if (prefs_ && !pref_name_.empty()) { + prefs_->RemovePrefObserver(pref_name_, this); + prefs_ = nullptr; + } +} + +void PrefMemberBase::MoveToSequence( + scoped_refptr<SequencedTaskRunner> task_runner) { + VerifyValuePrefName(); + // Load the value from preferences if it hasn't been loaded so far. + if (!internal()) + UpdateValueFromPref(base::Closure()); + internal()->MoveToSequence(std::move(task_runner)); +} + +void PrefMemberBase::OnPreferenceChanged(PrefService* service, + const std::string& pref_name) { + VerifyValuePrefName(); + UpdateValueFromPref((!setting_value_ && !observer_.is_null()) ? + base::Bind(observer_, pref_name) : base::Closure()); +} + +void PrefMemberBase::UpdateValueFromPref(const base::Closure& callback) const { + VerifyValuePrefName(); + const PrefService::Preference* pref = prefs_->FindPreference(pref_name_); + DCHECK(pref); + if (!internal()) + CreateInternal(); + internal()->UpdateValue(pref->GetValue()->DeepCopy(), + pref->IsManaged(), + pref->IsUserModifiable(), + callback); +} + +void PrefMemberBase::VerifyPref() const { + VerifyValuePrefName(); + if (!internal()) + UpdateValueFromPref(base::Closure()); +} + +void PrefMemberBase::InvokeUnnamedCallback(const base::Closure& callback, + const std::string& pref_name) { + callback.Run(); +} + +PrefMemberBase::Internal::Internal() + : owning_task_runner_(base::SequencedTaskRunnerHandle::Get()), + is_managed_(false), + is_user_modifiable_(false) {} +PrefMemberBase::Internal::~Internal() { } + +bool PrefMemberBase::Internal::IsOnCorrectSequence() const { + return owning_task_runner_->RunsTasksInCurrentSequence(); +} + +void PrefMemberBase::Internal::UpdateValue(base::Value* v, + bool is_managed, + bool is_user_modifiable, + base::OnceClosure callback) const { + std::unique_ptr<base::Value> value(v); + base::ScopedClosureRunner closure_runner(std::move(callback)); + if (IsOnCorrectSequence()) { + bool rv = UpdateValueInternal(*value); + DCHECK(rv); + is_managed_ = is_managed; + is_user_modifiable_ = is_user_modifiable; + } else { + bool may_run = owning_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&PrefMemberBase::Internal::UpdateValue, this, + value.release(), is_managed, is_user_modifiable, + closure_runner.Release())); + DCHECK(may_run); + } +} + +void PrefMemberBase::Internal::MoveToSequence( + scoped_refptr<SequencedTaskRunner> task_runner) { + CheckOnCorrectSequence(); + owning_task_runner_ = std::move(task_runner); +} + +bool PrefMemberVectorStringUpdate(const base::Value& value, + std::vector<std::string>* string_vector) { + if (!value.is_list()) + return false; + const base::ListValue* list = static_cast<const base::ListValue*>(&value); + + std::vector<std::string> local_vector; + for (auto it = list->begin(); it != list->end(); ++it) { + std::string string_value; + if (!it->GetAsString(&string_value)) + return false; + + local_vector.push_back(string_value); + } + + string_vector->swap(local_vector); + return true; +} + +} // namespace subtle + +template <> +void PrefMember<bool>::UpdatePref(const bool& value) { + prefs()->SetBoolean(pref_name(), value); +} + +template <> +bool PrefMember<bool>::Internal::UpdateValueInternal( + const base::Value& value) const { + return value.GetAsBoolean(&value_); +} + +template <> +void PrefMember<int>::UpdatePref(const int& value) { + prefs()->SetInteger(pref_name(), value); +} + +template <> +bool PrefMember<int>::Internal::UpdateValueInternal( + const base::Value& value) const { + return value.GetAsInteger(&value_); +} + +template <> +void PrefMember<double>::UpdatePref(const double& value) { + prefs()->SetDouble(pref_name(), value); +} + +template <> +bool PrefMember<double>::Internal::UpdateValueInternal(const base::Value& value) + const { + return value.GetAsDouble(&value_); +} + +template <> +void PrefMember<std::string>::UpdatePref(const std::string& value) { + prefs()->SetString(pref_name(), value); +} + +template <> +bool PrefMember<std::string>::Internal::UpdateValueInternal( + const base::Value& value) + const { + return value.GetAsString(&value_); +} + +template <> +void PrefMember<base::FilePath>::UpdatePref(const base::FilePath& value) { + prefs()->SetFilePath(pref_name(), value); +} + +template <> +bool PrefMember<base::FilePath>::Internal::UpdateValueInternal( + const base::Value& value) + const { + return base::GetValueAsFilePath(value, &value_); +} + +template <> +void PrefMember<std::vector<std::string> >::UpdatePref( + const std::vector<std::string>& value) { + base::ListValue list_value; + list_value.AppendStrings(value); + prefs()->Set(pref_name(), list_value); +} + +template <> +bool PrefMember<std::vector<std::string> >::Internal::UpdateValueInternal( + const base::Value& value) const { + return subtle::PrefMemberVectorStringUpdate(value, &value_); +}
diff --git a/src/updater_components/prefs/pref_member.h b/src/updater_components/prefs/pref_member.h new file mode 100644 index 0000000..f2166e6 --- /dev/null +++ b/src/updater_components/prefs/pref_member.h
@@ -0,0 +1,355 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A helper class that stays in sync with a preference (bool, int, real, +// string or filepath). For example: +// +// class MyClass { +// public: +// MyClass(PrefService* prefs) { +// my_string_.Init(prefs::kHomePage, prefs); +// } +// private: +// StringPrefMember my_string_; +// }; +// +// my_string_ should stay in sync with the prefs::kHomePage pref and will +// update if either the pref changes or if my_string_.SetValue is called. +// +// An optional observer can be passed into the Init method which can be used to +// notify MyClass of changes. Note that if you use SetValue(), the observer +// will not be notified. + +#ifndef COMPONENTS_PREFS_PREF_MEMBER_H_ +#define COMPONENTS_PREFS_PREF_MEMBER_H_ + +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/callback_forward.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/sequenced_task_runner.h" +#include "base/values.h" +#include "components/prefs/pref_observer.h" +#include "components/prefs/prefs_export.h" + +class PrefService; + +namespace subtle { + +class COMPONENTS_PREFS_EXPORT PrefMemberBase : public PrefObserver { + public: + // Type of callback you can register if you need to know the name of + // the pref that is changing. + typedef base::Callback<void(const std::string&)> NamedChangeCallback; + + PrefService* prefs() { return prefs_; } + const PrefService* prefs() const { return prefs_; } + + protected: + class COMPONENTS_PREFS_EXPORT Internal + : public base::RefCountedThreadSafe<Internal> { + public: + Internal(); + + // Update the value, either by calling |UpdateValueInternal| directly + // or by dispatching to the right sequence. + // Takes ownership of |value|. + void UpdateValue(base::Value* value, + bool is_managed, + bool is_user_modifiable, + base::OnceClosure callback) const; + + void MoveToSequence(scoped_refptr<base::SequencedTaskRunner> task_runner); + + // See PrefMember<> for description. + bool IsManaged() const { + return is_managed_; + } + + bool IsUserModifiable() const { + return is_user_modifiable_; + } + + protected: + friend class base::RefCountedThreadSafe<Internal>; + virtual ~Internal(); + + void CheckOnCorrectSequence() const { DCHECK(IsOnCorrectSequence()); } + + private: + // This method actually updates the value. It should only be called from + // the sequence the PrefMember is on. + virtual bool UpdateValueInternal(const base::Value& value) const = 0; + + bool IsOnCorrectSequence() const; + + scoped_refptr<base::SequencedTaskRunner> owning_task_runner_; + mutable bool is_managed_; + mutable bool is_user_modifiable_; + + DISALLOW_COPY_AND_ASSIGN(Internal); + }; + + PrefMemberBase(); + virtual ~PrefMemberBase(); + + // See PrefMember<> for description. + void Init(const std::string& pref_name, + PrefService* prefs, + const NamedChangeCallback& observer); + void Init(const std::string& pref_name, PrefService* prefs); + + virtual void CreateInternal() const = 0; + + // See PrefMember<> for description. + void Destroy(); + + void MoveToSequence(scoped_refptr<base::SequencedTaskRunner> task_runner); + + // PrefObserver + void OnPreferenceChanged(PrefService* service, + const std::string& pref_name) override; + + void VerifyValuePrefName() const { + DCHECK(!pref_name_.empty()); + } + + // This method is used to do the actual sync with the preference. + // Note: it is logically const, because it doesn't modify the state + // seen by the outside world. It is just doing a lazy load behind the scenes. + void UpdateValueFromPref(const base::Closure& callback) const; + + // Verifies the preference name, and lazily loads the preference value if + // it hasn't been loaded yet. + void VerifyPref() const; + + const std::string& pref_name() const { return pref_name_; } + + virtual Internal* internal() const = 0; + + // Used to allow registering plain base::Closure callbacks. + static void InvokeUnnamedCallback(const base::Closure& callback, + const std::string& pref_name); + + private: + // Ordered the members to compact the class instance. + std::string pref_name_; + NamedChangeCallback observer_; + PrefService* prefs_; + + protected: + bool setting_value_; +}; + +// This function implements StringListPrefMember::UpdateValue(). +// It is exposed here for testing purposes. +bool COMPONENTS_PREFS_EXPORT PrefMemberVectorStringUpdate( + const base::Value& value, + std::vector<std::string>* string_vector); + +} // namespace subtle + +template <typename ValueType> +class PrefMember : public subtle::PrefMemberBase { + public: + // Defer initialization to an Init method so it's easy to make this class be + // a member variable. + PrefMember() {} + virtual ~PrefMember() {} + + // Do the actual initialization of the class. Use the two-parameter + // version if you don't want any notifications of changes. This + // method should only be called on the UI thread. + void Init(const std::string& pref_name, + PrefService* prefs, + const NamedChangeCallback& observer) { + subtle::PrefMemberBase::Init(pref_name, prefs, observer); + } + void Init(const std::string& pref_name, + PrefService* prefs, + const base::Closure& observer) { + subtle::PrefMemberBase::Init( + pref_name, prefs, + base::Bind(&PrefMemberBase::InvokeUnnamedCallback, observer)); + } + void Init(const std::string& pref_name, PrefService* prefs) { + subtle::PrefMemberBase::Init(pref_name, prefs); + } + + // Unsubscribes the PrefMember from the PrefService. After calling this + // function, the PrefMember may not be used any more on the UI thread. + // Assuming |MoveToSequence| was previously called, |GetValue|, |IsManaged|, + // and |IsUserModifiable| can still be called from the other sequence but + // the results will no longer update from the PrefService. + // This method should only be called on the UI thread. + void Destroy() { + subtle::PrefMemberBase::Destroy(); + } + + // Moves the PrefMember to another sequence, allowing read accesses from + // there. Changes from the PrefService will be propagated asynchronously + // via PostTask. + // This method should only be used from the sequence the PrefMember is + // currently on, which is the UI thread by default. + void MoveToSequence(scoped_refptr<base::SequencedTaskRunner> task_runner) { + subtle::PrefMemberBase::MoveToSequence(task_runner); + } + + // Check whether the pref is managed, i.e. controlled externally through + // enterprise configuration management (e.g. windows group policy). Returns + // false for unknown prefs. + // This method should only be used from the sequence the PrefMember is + // currently on, which is the UI thread unless changed by |MoveToSequence|. + bool IsManaged() const { + VerifyPref(); + return internal_->IsManaged(); + } + + // Checks whether the pref can be modified by the user. This returns false + // when the pref is managed by a policy or an extension, and when a command + // line flag overrides the pref. + // This method should only be used from the sequence the PrefMember is + // currently on, which is the UI thread unless changed by |MoveToSequence|. + bool IsUserModifiable() const { + VerifyPref(); + return internal_->IsUserModifiable(); + } + + // Retrieve the value of the member variable. + // This method should only be used from the sequence the PrefMember is + // currently on, which is the UI thread unless changed by |MoveToSequence|. + ValueType GetValue() const { + VerifyPref(); + return internal_->value(); + } + + // Provided as a convenience. + ValueType operator*() const { + return GetValue(); + } + + // Set the value of the member variable. + // This method should only be called on the UI thread. + void SetValue(const ValueType& value) { + VerifyValuePrefName(); + setting_value_ = true; + UpdatePref(value); + setting_value_ = false; + } + + // Returns the pref name. + const std::string& GetPrefName() const { + return pref_name(); + } + + private: + class Internal : public subtle::PrefMemberBase::Internal { + public: + Internal() : value_(ValueType()) {} + + ValueType value() { + CheckOnCorrectSequence(); + return value_; + } + + protected: + ~Internal() override {} + + COMPONENTS_PREFS_EXPORT bool UpdateValueInternal( + const base::Value& value) const override; + + // We cache the value of the pref so we don't have to keep walking the pref + // tree. + mutable ValueType value_; + + private: + DISALLOW_COPY_AND_ASSIGN(Internal); + }; + + Internal* internal() const override { return internal_.get(); } + void CreateInternal() const override { internal_ = new Internal(); } + + // This method is used to do the actual sync with pref of the specified type. + void COMPONENTS_PREFS_EXPORT UpdatePref(const ValueType& value); + + mutable scoped_refptr<Internal> internal_; + + DISALLOW_COPY_AND_ASSIGN(PrefMember); +}; + +// Declaration of template specialization need to be repeated here +// specifically for each specialization (rather than just once above) +// or at least one of our compilers won't be happy in all cases. +// Specifically, it was failing on ChromeOS with a complaint about +// PrefMember<FilePath>::UpdateValueInternal not being defined when +// built in a chroot with the following parameters: +// +// FEATURES="noclean nostrip" USE="-chrome_debug -chrome_remoting +// -chrome_internal -chrome_pdf component_build" +// ~/trunk/goma/goma-wrapper cros_chrome_make --board=${BOARD} +// --install --runhooks + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<bool>::UpdatePref(const bool& value); + +template <> +COMPONENTS_PREFS_EXPORT bool PrefMember<bool>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<int>::UpdatePref(const int& value); + +template <> +COMPONENTS_PREFS_EXPORT bool PrefMember<int>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void +PrefMember<double>::UpdatePref(const double& value); + +template <> +COMPONENTS_PREFS_EXPORT bool PrefMember<double>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<std::string>::UpdatePref( + const std::string& value); + +template <> +COMPONENTS_PREFS_EXPORT bool +PrefMember<std::string>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<base::FilePath>::UpdatePref( + const base::FilePath& value); + +template <> +COMPONENTS_PREFS_EXPORT bool +PrefMember<base::FilePath>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<std::vector<std::string>>::UpdatePref( + const std::vector<std::string>& value); + +template <> +COMPONENTS_PREFS_EXPORT bool +PrefMember<std::vector<std::string>>::Internal::UpdateValueInternal( + const base::Value& value) const; + +typedef PrefMember<bool> BooleanPrefMember; +typedef PrefMember<int> IntegerPrefMember; +typedef PrefMember<double> DoublePrefMember; +typedef PrefMember<std::string> StringPrefMember; +typedef PrefMember<base::FilePath> FilePathPrefMember; +// This preference member is expensive for large string arrays. +typedef PrefMember<std::vector<std::string>> StringListPrefMember; + +#endif // COMPONENTS_PREFS_PREF_MEMBER_H_
diff --git a/src/updater_components/prefs/pref_member_unittest.cc b/src/updater_components/prefs/pref_member_unittest.cc new file mode 100644 index 0000000..126b18f --- /dev/null +++ b/src/updater_components/prefs/pref_member_unittest.cc
@@ -0,0 +1,322 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_member.h" + +#include <memory> + +#include "base/bind.h" +#include "base/location.h" +#include "base/sequenced_task_runner.h" +#include "base/synchronization/waitable_event.h" +#include "base/task/post_task.h" +#include "base/test/task_environment.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kBoolPref[] = "bool"; +const char kIntPref[] = "int"; +const char kDoublePref[] = "double"; +const char kStringPref[] = "string"; +const char kStringListPref[] = "string_list"; + +void RegisterTestPrefs(PrefRegistrySimple* registry) { + registry->RegisterBooleanPref(kBoolPref, false); + registry->RegisterIntegerPref(kIntPref, 0); + registry->RegisterDoublePref(kDoublePref, 0.0); + registry->RegisterStringPref(kStringPref, "default"); + registry->RegisterListPref(kStringListPref); +} + +class GetPrefValueHelper + : public base::RefCountedThreadSafe<GetPrefValueHelper> { + public: + GetPrefValueHelper() + : value_(false), + task_runner_(base::CreateSequencedTaskRunner({base::ThreadPool()})) {} + + void Init(const std::string& pref_name, PrefService* prefs) { + pref_.Init(pref_name, prefs); + pref_.MoveToSequence(task_runner_); + } + + void Destroy() { + pref_.Destroy(); + } + + void FetchValue() { + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + ASSERT_TRUE(task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&GetPrefValueHelper::GetPrefValue, this, &event))); + event.Wait(); + } + + bool value() { return value_; } + + private: + friend class base::RefCountedThreadSafe<GetPrefValueHelper>; + ~GetPrefValueHelper() {} + + void GetPrefValue(base::WaitableEvent* event) { + value_ = pref_.GetValue(); + event->Signal(); + } + + BooleanPrefMember pref_; + bool value_; + + // The sequence |pref_| runs on. + scoped_refptr<base::SequencedTaskRunner> task_runner_; +}; + +class PrefMemberTestClass { + public: + explicit PrefMemberTestClass(PrefService* prefs) + : observe_cnt_(0), prefs_(prefs) { + str_.Init(kStringPref, prefs, + base::Bind(&PrefMemberTestClass::OnPreferenceChanged, + base::Unretained(this))); + } + + void OnPreferenceChanged(const std::string& pref_name) { + EXPECT_EQ(pref_name, kStringPref); + EXPECT_EQ(str_.GetValue(), prefs_->GetString(kStringPref)); + ++observe_cnt_; + } + + StringPrefMember str_; + int observe_cnt_; + + private: + PrefService* prefs_; +}; + +} // anonymous namespace + +class PrefMemberTest : public testing::Test { + base::test::TaskEnvironment task_environment_; +}; + +TEST_F(PrefMemberTest, BasicGetAndSet) { + TestingPrefServiceSimple prefs; + RegisterTestPrefs(prefs.registry()); + + // Test bool + BooleanPrefMember boolean; + boolean.Init(kBoolPref, &prefs); + + // Check the defaults + EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); + EXPECT_FALSE(boolean.GetValue()); + EXPECT_FALSE(*boolean); + + // Try changing through the member variable. + boolean.SetValue(true); + EXPECT_TRUE(boolean.GetValue()); + EXPECT_TRUE(prefs.GetBoolean(kBoolPref)); + EXPECT_TRUE(*boolean); + + // Try changing back through the pref. + prefs.SetBoolean(kBoolPref, false); + EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); + EXPECT_FALSE(boolean.GetValue()); + EXPECT_FALSE(*boolean); + + // Test int + IntegerPrefMember integer; + integer.Init(kIntPref, &prefs); + + // Check the defaults + EXPECT_EQ(0, prefs.GetInteger(kIntPref)); + EXPECT_EQ(0, integer.GetValue()); + EXPECT_EQ(0, *integer); + + // Try changing through the member variable. + integer.SetValue(5); + EXPECT_EQ(5, integer.GetValue()); + EXPECT_EQ(5, prefs.GetInteger(kIntPref)); + EXPECT_EQ(5, *integer); + + // Try changing back through the pref. + prefs.SetInteger(kIntPref, 2); + EXPECT_EQ(2, prefs.GetInteger(kIntPref)); + EXPECT_EQ(2, integer.GetValue()); + EXPECT_EQ(2, *integer); + + // Test double + DoublePrefMember double_member; + double_member.Init(kDoublePref, &prefs); + + // Check the defaults + EXPECT_EQ(0.0, prefs.GetDouble(kDoublePref)); + EXPECT_EQ(0.0, double_member.GetValue()); + EXPECT_EQ(0.0, *double_member); + + // Try changing through the member variable. + double_member.SetValue(1.0); + EXPECT_EQ(1.0, double_member.GetValue()); + EXPECT_EQ(1.0, prefs.GetDouble(kDoublePref)); + EXPECT_EQ(1.0, *double_member); + + // Try changing back through the pref. + prefs.SetDouble(kDoublePref, 3.0); + EXPECT_EQ(3.0, prefs.GetDouble(kDoublePref)); + EXPECT_EQ(3.0, double_member.GetValue()); + EXPECT_EQ(3.0, *double_member); + + // Test string + StringPrefMember string; + string.Init(kStringPref, &prefs); + + // Check the defaults + EXPECT_EQ("default", prefs.GetString(kStringPref)); + EXPECT_EQ("default", string.GetValue()); + EXPECT_EQ("default", *string); + + // Try changing through the member variable. + string.SetValue("foo"); + EXPECT_EQ("foo", string.GetValue()); + EXPECT_EQ("foo", prefs.GetString(kStringPref)); + EXPECT_EQ("foo", *string); + + // Try changing back through the pref. + prefs.SetString(kStringPref, "bar"); + EXPECT_EQ("bar", prefs.GetString(kStringPref)); + EXPECT_EQ("bar", string.GetValue()); + EXPECT_EQ("bar", *string); + + // Test string list + base::ListValue expected_list; + std::vector<std::string> expected_vector; + StringListPrefMember string_list; + string_list.Init(kStringListPref, &prefs); + + // Check the defaults + EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); + EXPECT_EQ(expected_vector, string_list.GetValue()); + EXPECT_EQ(expected_vector, *string_list); + + // Try changing through the pref member. + expected_list.AppendString("foo"); + expected_vector.push_back("foo"); + string_list.SetValue(expected_vector); + + EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); + EXPECT_EQ(expected_vector, string_list.GetValue()); + EXPECT_EQ(expected_vector, *string_list); + + // Try adding through the pref. + expected_list.AppendString("bar"); + expected_vector.push_back("bar"); + prefs.Set(kStringListPref, expected_list); + + EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); + EXPECT_EQ(expected_vector, string_list.GetValue()); + EXPECT_EQ(expected_vector, *string_list); + + // Try removing through the pref. + expected_list.Remove(0, nullptr); + expected_vector.erase(expected_vector.begin()); + prefs.Set(kStringListPref, expected_list); + + EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); + EXPECT_EQ(expected_vector, string_list.GetValue()); + EXPECT_EQ(expected_vector, *string_list); +} + +TEST_F(PrefMemberTest, InvalidList) { + // Set the vector to an initial good value. + std::vector<std::string> expected_vector; + expected_vector.push_back("foo"); + + // Try to add a valid list first. + base::ListValue list; + list.AppendString("foo"); + std::vector<std::string> vector; + EXPECT_TRUE(subtle::PrefMemberVectorStringUpdate(list, &vector)); + EXPECT_EQ(expected_vector, vector); + + // Now try to add an invalid list. |vector| should not be changed. + list.AppendInteger(0); + EXPECT_FALSE(subtle::PrefMemberVectorStringUpdate(list, &vector)); + EXPECT_EQ(expected_vector, vector); +} + +TEST_F(PrefMemberTest, TwoPrefs) { + // Make sure two DoublePrefMembers stay in sync. + TestingPrefServiceSimple prefs; + RegisterTestPrefs(prefs.registry()); + + DoublePrefMember pref1; + pref1.Init(kDoublePref, &prefs); + DoublePrefMember pref2; + pref2.Init(kDoublePref, &prefs); + + pref1.SetValue(2.3); + EXPECT_EQ(2.3, *pref2); + + pref2.SetValue(3.5); + EXPECT_EQ(3.5, *pref1); + + prefs.SetDouble(kDoublePref, 4.2); + EXPECT_EQ(4.2, *pref1); + EXPECT_EQ(4.2, *pref2); +} + +TEST_F(PrefMemberTest, Observer) { + TestingPrefServiceSimple prefs; + RegisterTestPrefs(prefs.registry()); + + PrefMemberTestClass test_obj(&prefs); + EXPECT_EQ("default", *test_obj.str_); + + // Calling SetValue should not fire the observer. + test_obj.str_.SetValue("hello"); + EXPECT_EQ(0, test_obj.observe_cnt_); + EXPECT_EQ("hello", prefs.GetString(kStringPref)); + + // Changing the pref does fire the observer. + prefs.SetString(kStringPref, "world"); + EXPECT_EQ(1, test_obj.observe_cnt_); + EXPECT_EQ("world", *(test_obj.str_)); + + // Not changing the value should not fire the observer. + prefs.SetString(kStringPref, "world"); + EXPECT_EQ(1, test_obj.observe_cnt_); + EXPECT_EQ("world", *(test_obj.str_)); + + prefs.SetString(kStringPref, "hello"); + EXPECT_EQ(2, test_obj.observe_cnt_); + EXPECT_EQ("hello", prefs.GetString(kStringPref)); +} + +TEST_F(PrefMemberTest, NoInit) { + // Make sure not calling Init on a PrefMember doesn't cause problems. + IntegerPrefMember pref; +} + +TEST_F(PrefMemberTest, MoveToSequence) { + TestingPrefServiceSimple prefs; + scoped_refptr<GetPrefValueHelper> helper(new GetPrefValueHelper()); + RegisterTestPrefs(prefs.registry()); + helper->Init(kBoolPref, &prefs); + + helper->FetchValue(); + EXPECT_FALSE(helper->value()); + + prefs.SetBoolean(kBoolPref, true); + + helper->FetchValue(); + EXPECT_TRUE(helper->value()); + + helper->Destroy(); + + helper->FetchValue(); + EXPECT_TRUE(helper->value()); +}
diff --git a/src/updater_components/prefs/pref_notifier.h b/src/updater_components/prefs/pref_notifier.h new file mode 100644 index 0000000..2abc213 --- /dev/null +++ b/src/updater_components/prefs/pref_notifier.h
@@ -0,0 +1,26 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_NOTIFIER_H_ +#define COMPONENTS_PREFS_PREF_NOTIFIER_H_ + +#include <string> + +// Delegate interface used by PrefValueStore to notify its owner about changes +// to the preference values. +// TODO(mnissler, danno): Move this declaration to pref_value_store.h once we've +// cleaned up all public uses of this interface. +class PrefNotifier { + public: + virtual ~PrefNotifier() {} + + // Sends out a change notification for the preference identified by + // |pref_name|. + virtual void OnPreferenceChanged(const std::string& pref_name) = 0; + + // Broadcasts the intialization completed notification. + virtual void OnInitializationCompleted(bool succeeded) = 0; +}; + +#endif // COMPONENTS_PREFS_PREF_NOTIFIER_H_
diff --git a/src/updater_components/prefs/pref_notifier_impl.cc b/src/updater_components/prefs/pref_notifier_impl.cc new file mode 100644 index 0000000..9c753e7 --- /dev/null +++ b/src/updater_components/prefs/pref_notifier_impl.cc
@@ -0,0 +1,150 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_notifier_impl.h" + +#include "base/debug/alias.h" +#include "base/debug/dump_without_crashing.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/strings/strcat.h" +#include "components/prefs/pref_service.h" + +PrefNotifierImpl::PrefNotifierImpl() : pref_service_(nullptr) {} + +PrefNotifierImpl::PrefNotifierImpl(PrefService* service) + : pref_service_(service) { +} + +PrefNotifierImpl::~PrefNotifierImpl() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Verify that there are no pref observers when we shut down. + for (const auto& observer_list : pref_observers_) { + if (observer_list.second->begin() != observer_list.second->end()) { + // Generally, there should not be any subscribers left when the profile + // is destroyed because a) those may indicate that the subscriber class + // maintains an active pointer to the profile that might be used for + // accessing a destroyed profile and b) those subscribers will try to + // unsubscribe from a PrefService that has been destroyed with the + // profile. + // There is one exception that is safe: Static objects that are leaked + // on process termination, if these objects just subscribe to preferences + // and never access the profile after destruction. As these objects are + // leaked on termination, it is guaranteed that they don't attempt to + // unsubscribe. + const auto& pref_name = observer_list.first; + std::string message = base::StrCat( + {"Pref observer for ", pref_name, " found at shutdown."}); + LOG(WARNING) << message; + DEBUG_ALIAS_FOR_CSTR(aliased_message, message.c_str(), 128); + + // TODO(crbug.com/942491, 946668, 945772) The following code collects + // stacktraces that show how the profile is destroyed that owns + // preferences which are known to have subscriptions outliving the + // profile. + if ( + // For GlobalMenuBarX11, crbug.com/946668 + pref_name == "bookmark_bar.show_on_all_tabs" || + // For BrowserWindowPropertyManager, crbug.com/942491 + pref_name == "profile.icon_version" || + // For BrowserWindowDefaultTouchBar, crbug.com/945772 + pref_name == "default_search_provider_data.template_url_data") { + base::debug::DumpWithoutCrashing(); + } + } + } + + // Same for initialization observers. + if (!init_observers_.empty()) + LOG(WARNING) << "Init observer found at shutdown."; + + pref_observers_.clear(); + init_observers_.clear(); +} + +void PrefNotifierImpl::AddPrefObserver(const std::string& path, + PrefObserver* obs) { + // Get the pref observer list associated with the path. + PrefObserverList* observer_list = nullptr; + auto observer_iterator = pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + observer_list = new PrefObserverList; + pref_observers_[path] = base::WrapUnique(observer_list); + } else { + observer_list = observer_iterator->second.get(); + } + + // Add the pref observer. ObserverList will DCHECK if it already is + // in the list. + observer_list->AddObserver(obs); +} + +void PrefNotifierImpl::RemovePrefObserver(const std::string& path, + PrefObserver* obs) { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto observer_iterator = pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + return; + } + + PrefObserverList* observer_list = observer_iterator->second.get(); + observer_list->RemoveObserver(obs); +} + +void PrefNotifierImpl::AddPrefObserverAllPrefs(PrefObserver* observer) { + DCHECK(thread_checker_.CalledOnValidThread()); + all_prefs_pref_observers_.AddObserver(observer); +} + +void PrefNotifierImpl::RemovePrefObserverAllPrefs(PrefObserver* observer) { + DCHECK(thread_checker_.CalledOnValidThread()); + all_prefs_pref_observers_.RemoveObserver(observer); +} + +void PrefNotifierImpl::AddInitObserver(base::OnceCallback<void(bool)> obs) { + init_observers_.push_back(std::move(obs)); +} + +void PrefNotifierImpl::OnPreferenceChanged(const std::string& path) { + FireObservers(path); +} + +void PrefNotifierImpl::OnInitializationCompleted(bool succeeded) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // We must move init_observers_ to a local variable before we run + // observers, or we can end up in this method re-entrantly before + // clearing the observers list. + PrefInitObserverList observers; + std::swap(observers, init_observers_); + + for (auto& observer : observers) + std::move(observer).Run(succeeded); +} + +void PrefNotifierImpl::FireObservers(const std::string& path) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Only send notifications for registered preferences. + if (!pref_service_->FindPreference(path)) + return; + + // Fire observers for any preference change. + for (auto& observer : all_prefs_pref_observers_) + observer.OnPreferenceChanged(pref_service_, path); + + auto observer_iterator = pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) + return; + + for (PrefObserver& observer : *(observer_iterator->second)) + observer.OnPreferenceChanged(pref_service_, path); +} + +void PrefNotifierImpl::SetPrefService(PrefService* pref_service) { + DCHECK(pref_service_ == nullptr); + pref_service_ = pref_service; +}
diff --git a/src/updater_components/prefs/pref_notifier_impl.h b/src/updater_components/prefs/pref_notifier_impl.h new file mode 100644 index 0000000..c550103 --- /dev/null +++ b/src/updater_components/prefs/pref_notifier_impl.h
@@ -0,0 +1,87 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_ +#define COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_ + +#include <list> +#include <memory> +#include <string> +#include <unordered_map> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/observer_list.h" +#include "base/threading/thread_checker.h" +#include "components/prefs/pref_notifier.h" +#include "components/prefs/pref_observer.h" +#include "components/prefs/prefs_export.h" + +class PrefService; + +// The PrefNotifier implementation used by the PrefService. +class COMPONENTS_PREFS_EXPORT PrefNotifierImpl : public PrefNotifier { + public: + PrefNotifierImpl(); + explicit PrefNotifierImpl(PrefService* pref_service); + ~PrefNotifierImpl() override; + + // If the pref at the given path changes, we call the observer's + // OnPreferenceChanged method. + void AddPrefObserver(const std::string& path, PrefObserver* observer); + void RemovePrefObserver(const std::string& path, PrefObserver* observer); + + // These observers are called for any pref changes. + // + // AVOID ADDING THESE. See the long comment in the identically-named + // functions on PrefService for background. + void AddPrefObserverAllPrefs(PrefObserver* observer); + void RemovePrefObserverAllPrefs(PrefObserver* observer); + + // We run the callback once, when initialization completes. The bool + // parameter will be set to true for successful initialization, + // false for unsuccessful. + void AddInitObserver(base::OnceCallback<void(bool)> observer); + + void SetPrefService(PrefService* pref_service); + + // PrefNotifier overrides. + void OnPreferenceChanged(const std::string& pref_name) override; + + protected: + // PrefNotifier overrides. + void OnInitializationCompleted(bool succeeded) override; + + // A map from pref names to a list of observers. Observers get fired in the + // order they are added. These should only be accessed externally for unit + // testing. + typedef base::ObserverList<PrefObserver>::Unchecked PrefObserverList; + typedef std::unordered_map<std::string, std::unique_ptr<PrefObserverList>> + PrefObserverMap; + + typedef std::list<base::OnceCallback<void(bool)>> PrefInitObserverList; + + const PrefObserverMap* pref_observers() const { return &pref_observers_; } + + private: + // For the given pref_name, fire any observer of the pref. Virtual so it can + // be mocked for unit testing. + virtual void FireObservers(const std::string& path); + + // Weak reference; the notifier is owned by the PrefService. + PrefService* pref_service_; + + PrefObserverMap pref_observers_; + PrefInitObserverList init_observers_; + + // Observers for changes to any preference. + PrefObserverList all_prefs_pref_observers_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(PrefNotifierImpl); +}; + +#endif // COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_
diff --git a/src/updater_components/prefs/pref_notifier_impl_unittest.cc b/src/updater_components/prefs/pref_notifier_impl_unittest.cc new file mode 100644 index 0000000..ee42891 --- /dev/null +++ b/src/updater_components/prefs/pref_notifier_impl_unittest.cc
@@ -0,0 +1,218 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> + +#include "base/bind.h" +#include "base/callback.h" +#include "components/prefs/mock_pref_change_callback.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_observer.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/pref_value_store.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Field; +using testing::Invoke; +using testing::Mock; +using testing::Truly; + +namespace { + +const char kChangedPref[] = "changed_pref"; +const char kUnchangedPref[] = "unchanged_pref"; + +class MockPrefInitObserver { + public: + MOCK_METHOD1(OnInitializationCompleted, void(bool)); +}; + +// This is an unmodified PrefNotifierImpl, except we make +// OnPreferenceChanged public for tests. +class TestingPrefNotifierImpl : public PrefNotifierImpl { + public: + explicit TestingPrefNotifierImpl(PrefService* service) + : PrefNotifierImpl(service) { + } + + // Make public for tests. + using PrefNotifierImpl::OnPreferenceChanged; +}; + +// Mock PrefNotifier that allows tracking of observers and notifications. +class MockPrefNotifier : public PrefNotifierImpl { + public: + explicit MockPrefNotifier(PrefService* pref_service) + : PrefNotifierImpl(pref_service) {} + ~MockPrefNotifier() override {} + + MOCK_METHOD1(FireObservers, void(const std::string& path)); + + size_t CountObserver(const std::string& path, PrefObserver* obs) { + auto observer_iterator = pref_observers()->find(path); + if (observer_iterator == pref_observers()->end()) + return false; + + size_t count = 0; + for (auto& existing_obs : *observer_iterator->second) { + if (&existing_obs == obs) + count++; + } + + return count; + } + + // Make public for tests below. + using PrefNotifierImpl::OnPreferenceChanged; + using PrefNotifierImpl::OnInitializationCompleted; +}; + +class PrefObserverMock : public PrefObserver { + public: + PrefObserverMock() {} + virtual ~PrefObserverMock() {} + + MOCK_METHOD2(OnPreferenceChanged, void(PrefService*, const std::string&)); +}; + +// Test fixture class. +class PrefNotifierTest : public testing::Test { + protected: + void SetUp() override { + pref_service_.registry()->RegisterBooleanPref(kChangedPref, true); + pref_service_.registry()->RegisterBooleanPref(kUnchangedPref, true); + } + + TestingPrefServiceSimple pref_service_; + + PrefObserverMock obs1_; + PrefObserverMock obs2_; +}; + +TEST_F(PrefNotifierTest, OnPreferenceChanged) { + MockPrefNotifier notifier(&pref_service_); + EXPECT_CALL(notifier, FireObservers(kChangedPref)).Times(1); + notifier.OnPreferenceChanged(kChangedPref); +} + +TEST_F(PrefNotifierTest, OnInitializationCompleted) { + MockPrefNotifier notifier(&pref_service_); + MockPrefInitObserver observer; + notifier.AddInitObserver( + base::BindOnce(&MockPrefInitObserver::OnInitializationCompleted, + base::Unretained(&observer))); + EXPECT_CALL(observer, OnInitializationCompleted(true)); + notifier.OnInitializationCompleted(true); +} + +TEST_F(PrefNotifierTest, AddAndRemovePrefObservers) { + const char pref_name[] = "homepage"; + const char pref_name2[] = "proxy"; + + MockPrefNotifier notifier(&pref_service_); + notifier.AddPrefObserver(pref_name, &obs1_); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + // Re-adding the same observer for the same pref doesn't change anything. + // Skip this in debug mode, since it hits a DCHECK and death tests aren't + // thread-safe. +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) + notifier.AddPrefObserver(pref_name, &obs1_); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); +#endif + + // Ensure that we can add the same observer to a different pref. + notifier.AddPrefObserver(pref_name2, &obs1_); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + // Ensure that we can add another observer to the same pref. + notifier.AddPrefObserver(pref_name, &obs2_); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + // Ensure that we can remove all observers, and that removing a non-existent + // observer is harmless. + notifier.RemovePrefObserver(pref_name, &obs1_); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + notifier.RemovePrefObserver(pref_name, &obs2_); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + notifier.RemovePrefObserver(pref_name, &obs1_); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + notifier.RemovePrefObserver(pref_name2, &obs1_); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); +} + +TEST_F(PrefNotifierTest, FireObservers) { + TestingPrefNotifierImpl notifier(&pref_service_); + notifier.AddPrefObserver(kChangedPref, &obs1_); + notifier.AddPrefObserver(kUnchangedPref, &obs1_); + + EXPECT_CALL(obs1_, OnPreferenceChanged(&pref_service_, kChangedPref)); + EXPECT_CALL(obs2_, OnPreferenceChanged(_, _)).Times(0); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + notifier.AddPrefObserver(kChangedPref, &obs2_); + notifier.AddPrefObserver(kUnchangedPref, &obs2_); + + EXPECT_CALL(obs1_, OnPreferenceChanged(&pref_service_, kChangedPref)); + EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref)); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + // Make sure removing an observer from one pref doesn't affect anything else. + notifier.RemovePrefObserver(kChangedPref, &obs1_); + + EXPECT_CALL(obs1_, OnPreferenceChanged(_, _)).Times(0); + EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref)); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + // Make sure removing an observer entirely doesn't affect anything else. + notifier.RemovePrefObserver(kUnchangedPref, &obs1_); + + EXPECT_CALL(obs1_, OnPreferenceChanged(_, _)).Times(0); + EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref)); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + notifier.RemovePrefObserver(kChangedPref, &obs2_); + notifier.RemovePrefObserver(kUnchangedPref, &obs2_); +} + +} // namespace
diff --git a/src/updater_components/prefs/pref_observer.h b/src/updater_components/prefs/pref_observer.h new file mode 100644 index 0000000..3f48931 --- /dev/null +++ b/src/updater_components/prefs/pref_observer.h
@@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_OBSERVER_H_ +#define COMPONENTS_PREFS_PREF_OBSERVER_H_ + +#include <string> + +class PrefService; + +// Used internally to the Prefs subsystem to pass preference change +// notifications between PrefService, PrefNotifierImpl and +// PrefChangeRegistrar. +class PrefObserver { + public: + virtual void OnPreferenceChanged(PrefService* service, + const std::string& pref_name) = 0; +}; + +#endif // COMPONENTS_PREFS_PREF_OBSERVER_H_
diff --git a/src/updater_components/prefs/pref_registry.cc b/src/updater_components/prefs/pref_registry.cc new file mode 100644 index 0000000..6c6aab7 --- /dev/null +++ b/src/updater_components/prefs/pref_registry.cc
@@ -0,0 +1,83 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_registry.h" + +#include <utility> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/values.h" +#include "components/prefs/default_pref_store.h" +#include "components/prefs/pref_store.h" + +PrefRegistry::PrefRegistry() + : defaults_(base::MakeRefCounted<DefaultPrefStore>()) {} + +PrefRegistry::~PrefRegistry() { +} + +uint32_t PrefRegistry::GetRegistrationFlags( + const std::string& pref_name) const { + const auto& it = registration_flags_.find(pref_name); + return it != registration_flags_.end() ? it->second : NO_REGISTRATION_FLAGS; +} + +scoped_refptr<PrefStore> PrefRegistry::defaults() { + return defaults_.get(); +} + +PrefRegistry::const_iterator PrefRegistry::begin() const { + return defaults_->begin(); +} + +PrefRegistry::const_iterator PrefRegistry::end() const { + return defaults_->end(); +} + +void PrefRegistry::SetDefaultPrefValue(const std::string& pref_name, + base::Value value) { + const base::Value* current_value = nullptr; + DCHECK(defaults_->GetValue(pref_name, ¤t_value)) + << "Setting default for unregistered pref: " << pref_name; + DCHECK(value.type() == current_value->type()) + << "Wrong type for new default: " << pref_name; + + defaults_->ReplaceDefaultValue(pref_name, std::move(value)); +} + +void PrefRegistry::SetDefaultForeignPrefValue(const std::string& path, + base::Value default_value, + uint32_t flags) { + auto erased = foreign_pref_keys_.erase(path); + DCHECK_EQ(1u, erased); + RegisterPreference(path, std::move(default_value), flags); +} + +void PrefRegistry::RegisterPreference(const std::string& path, + base::Value default_value, + uint32_t flags) { + base::Value::Type orig_type = default_value.type(); + DCHECK(orig_type != base::Value::Type::NONE && + orig_type != base::Value::Type::BINARY) << + "invalid preference type: " << orig_type; + DCHECK(!defaults_->GetValue(path, nullptr)) + << "Trying to register a previously registered pref: " << path; + DCHECK(!base::Contains(registration_flags_, path)) + << "Trying to register a previously registered pref: " << path; + + defaults_->SetDefaultValue(path, std::move(default_value)); + if (flags != NO_REGISTRATION_FLAGS) + registration_flags_[path] = flags; + + OnPrefRegistered(path, flags); +} + +void PrefRegistry::RegisterForeignPref(const std::string& path) { + bool inserted = foreign_pref_keys_.insert(path).second; + DCHECK(inserted); +} + +void PrefRegistry::OnPrefRegistered(const std::string& path, + uint32_t flags) {}
diff --git a/src/updater_components/prefs/pref_registry.h b/src/updater_components/prefs/pref_registry.h new file mode 100644 index 0000000..065bd65 --- /dev/null +++ b/src/updater_components/prefs/pref_registry.h
@@ -0,0 +1,115 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_REGISTRY_H_ +#define COMPONENTS_PREFS_PREF_REGISTRY_H_ + +#include <stdint.h> + +#include <set> +#include <unordered_map> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/prefs/pref_value_map.h" +#include "components/prefs/prefs_export.h" + +namespace base { +class Value; +} + +class DefaultPrefStore; +class PrefStore; + +// Preferences need to be registered with a type and default value +// before they are used. +// +// The way you use a PrefRegistry is that you register all required +// preferences on it (via one of its subclasses), then pass it as a +// construction parameter to PrefService. +// +// Currently, registrations after constructing the PrefService will +// also work, but this is being deprecated. +class COMPONENTS_PREFS_EXPORT PrefRegistry + : public base::RefCounted<PrefRegistry> { + public: + // Registration flags that can be specified which impact how the pref will + // behave or be stored. This will be passed in a bitmask when the pref is + // registered. Subclasses of PrefRegistry can specify their own flags. Care + // must be taken to ensure none of these overlap with the flags below. + enum PrefRegistrationFlags : uint32_t { + // No flags are specified. + NO_REGISTRATION_FLAGS = 0, + + // The first 8 bits are reserved for subclasses of PrefRegistry to use. + + // This marks the pref as "lossy". There is no strict time guarantee on when + // a lossy pref will be persisted to permanent storage when it is modified. + LOSSY_PREF = 1 << 8, + + // Registering a pref as public allows other services to access it. + PUBLIC = 1 << 9, + }; + + typedef PrefValueMap::const_iterator const_iterator; + typedef std::unordered_map<std::string, uint32_t> PrefRegistrationFlagsMap; + + PrefRegistry(); + + // Retrieve the set of registration flags for the given preference. The return + // value is a bitmask of PrefRegistrationFlags. + uint32_t GetRegistrationFlags(const std::string& pref_name) const; + + // Gets the registered defaults. + scoped_refptr<PrefStore> defaults(); + + // Allows iteration over defaults. + const_iterator begin() const; + const_iterator end() const; + + // Changes the default value for a preference. + // + // |pref_name| must be a previously registered preference. + void SetDefaultPrefValue(const std::string& pref_name, base::Value value); + + // Registers a pref owned by another service for use with the current service. + // The owning service must register that pref with the |PUBLIC| flag. + void RegisterForeignPref(const std::string& path); + + // Sets the default value and flags of a previously-registered foreign pref + // value. + void SetDefaultForeignPrefValue(const std::string& path, + base::Value default_value, + uint32_t flags); + + const std::set<std::string>& foreign_pref_keys() const { + return foreign_pref_keys_; + } + + protected: + friend class base::RefCounted<PrefRegistry>; + virtual ~PrefRegistry(); + + // Used by subclasses to register a default value and registration flags for + // a preference. |flags| is a bitmask of |PrefRegistrationFlags|. + void RegisterPreference(const std::string& path, + base::Value default_value, + uint32_t flags); + + // Allows subclasses to hook into pref registration. + virtual void OnPrefRegistered(const std::string& path, + uint32_t flags); + + scoped_refptr<DefaultPrefStore> defaults_; + + // A map of pref name to a bitmask of PrefRegistrationFlags. + PrefRegistrationFlagsMap registration_flags_; + + std::set<std::string> foreign_pref_keys_; + + private: + DISALLOW_COPY_AND_ASSIGN(PrefRegistry); +}; + +#endif // COMPONENTS_PREFS_PREF_REGISTRY_H_
diff --git a/src/updater_components/prefs/pref_registry_simple.cc b/src/updater_components/prefs/pref_registry_simple.cc new file mode 100644 index 0000000..bab05dd --- /dev/null +++ b/src/updater_components/prefs/pref_registry_simple.cc
@@ -0,0 +1,94 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_registry_simple.h" + +#include <utility> + +#include "base/files/file_path.h" +#include "base/strings/string_number_conversions.h" +#include "base/values.h" + +PrefRegistrySimple::PrefRegistrySimple() = default; +PrefRegistrySimple::~PrefRegistrySimple() = default; + +void PrefRegistrySimple::RegisterBooleanPref(const std::string& path, + bool default_value, + uint32_t flags) { + RegisterPreference(path, base::Value(default_value), flags); +} + +void PrefRegistrySimple::RegisterIntegerPref(const std::string& path, + int default_value, + uint32_t flags) { + RegisterPreference(path, base::Value(default_value), flags); +} + +void PrefRegistrySimple::RegisterDoublePref(const std::string& path, + double default_value, + uint32_t flags) { + RegisterPreference(path, base::Value(default_value), flags); +} + +void PrefRegistrySimple::RegisterStringPref(const std::string& path, + const std::string& default_value, + uint32_t flags) { + RegisterPreference(path, base::Value(default_value), flags); +} + +void PrefRegistrySimple::RegisterFilePathPref( + const std::string& path, + const base::FilePath& default_value, + uint32_t flags) { + RegisterPreference(path, base::Value(default_value.value()), flags); +} + +void PrefRegistrySimple::RegisterListPref(const std::string& path, + uint32_t flags) { + RegisterPreference(path, base::Value(base::Value::Type::LIST), flags); +} + +void PrefRegistrySimple::RegisterListPref(const std::string& path, + base::Value default_value, + uint32_t flags) { + RegisterPreference(path, std::move(default_value), flags); +} + +void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path, + uint32_t flags) { + RegisterPreference(path, base::Value(base::Value::Type::DICTIONARY), flags); +} + +void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path, + base::Value default_value, + uint32_t flags) { + RegisterPreference(path, std::move(default_value), flags); +} + +void PrefRegistrySimple::RegisterInt64Pref(const std::string& path, + int64_t default_value, + uint32_t flags) { + RegisterPreference(path, base::Value(base::NumberToString(default_value)), + flags); +} + +void PrefRegistrySimple::RegisterUint64Pref(const std::string& path, + uint64_t default_value, + uint32_t flags) { + RegisterPreference(path, base::Value(base::NumberToString(default_value)), + flags); +} + +void PrefRegistrySimple::RegisterTimePref(const std::string& path, + base::Time default_value, + uint32_t flags) { + RegisterInt64Pref( + path, default_value.ToDeltaSinceWindowsEpoch().InMicroseconds(), flags); +} + +void PrefRegistrySimple::RegisterTimeDeltaPref(const std::string& path, + base::TimeDelta default_value, + uint32_t flags) { + RegisterInt64Pref(path, default_value.InMicroseconds(), flags); +}
diff --git a/src/updater_components/prefs/pref_registry_simple.h b/src/updater_components/prefs/pref_registry_simple.h new file mode 100644 index 0000000..b167e94 --- /dev/null +++ b/src/updater_components/prefs/pref_registry_simple.h
@@ -0,0 +1,87 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_ +#define COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_ + +#include <stdint.h> + +#include <memory> +#include <string> + +#include "base/macros.h" +#include "base/time/time.h" +#include "components/prefs/pref_registry.h" +#include "components/prefs/prefs_export.h" + +namespace base { +class Value; +class FilePath; +} + +// A simple implementation of PrefRegistry. +class COMPONENTS_PREFS_EXPORT PrefRegistrySimple : public PrefRegistry { + public: + PrefRegistrySimple(); + + // For each of these registration methods, |flags| is an optional bitmask of + // PrefRegistrationFlags. + void RegisterBooleanPref(const std::string& path, + bool default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterIntegerPref(const std::string& path, + int default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterDoublePref(const std::string& path, + double default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterStringPref(const std::string& path, + const std::string& default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterFilePathPref(const std::string& path, + const base::FilePath& default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterListPref(const std::string& path, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterListPref(const std::string& path, + base::Value default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterDictionaryPref(const std::string& path, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterDictionaryPref(const std::string& path, + base::Value default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterInt64Pref(const std::string& path, + int64_t default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterUint64Pref(const std::string& path, + uint64_t default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterTimePref(const std::string& path, + base::Time default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + void RegisterTimeDeltaPref(const std::string& path, + base::TimeDelta default_value, + uint32_t flags = NO_REGISTRATION_FLAGS); + + protected: + ~PrefRegistrySimple() override; + + private: + DISALLOW_COPY_AND_ASSIGN(PrefRegistrySimple); +}; + +#endif // COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_
diff --git a/src/updater_components/prefs/pref_service.cc b/src/updater_components/prefs/pref_service.cc new file mode 100644 index 0000000..f0796fe --- /dev/null +++ b/src/updater_components/prefs/pref_service.cc
@@ -0,0 +1,732 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_service.h" + +#include <algorithm> +#include <map> +#include <utility> + +#include "base/bind.h" +#include "base/debug/alias.h" +#include "base/debug/dump_without_crashing.h" +#include "base/files/file_path.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/metrics/histogram.h" +#include "base/single_thread_task_runner.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/util/values/values_util.h" +#include "base/value_conversions.h" +#include "build/build_config.h" +#include "components/prefs/default_pref_store.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_registry.h" + +namespace { + +class ReadErrorHandler : public PersistentPrefStore::ReadErrorDelegate { + public: + using ErrorCallback = + base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>; + explicit ReadErrorHandler(ErrorCallback cb) : callback_(cb) {} + + void OnError(PersistentPrefStore::PrefReadError error) override { + callback_.Run(error); + } + + private: + ErrorCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(ReadErrorHandler); +}; + +// Returns the WriteablePrefStore::PrefWriteFlags for the pref with the given +// |path|. +uint32_t GetWriteFlags(const PrefService::Preference* pref) { + uint32_t write_flags = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS; + + if (!pref) + return write_flags; + + if (pref->registration_flags() & PrefRegistry::LOSSY_PREF) + write_flags |= WriteablePrefStore::LOSSY_PREF_WRITE_FLAG; + return write_flags; +} + +// For prefs names in |pref_store| that are not presented in |pref_changed_map|, +// check if their values differ from those in pref_service->FindPreference() and +// add the result into |pref_changed_map|. +void CheckForNewPrefChangesInPrefStore( + std::map<std::string, bool>* pref_changed_map, + PrefStore* pref_store, + PrefService* pref_service) { + if (!pref_store) + return; + auto values = pref_store->GetValues(); + for (const auto& item : values->DictItems()) { + // If the key already presents, skip it as a store with higher precedence + // already sets the entry. + if (pref_changed_map->find(item.first) != pref_changed_map->end()) + continue; + const PrefService::Preference* pref = + pref_service->FindPreference(item.first); + if (!pref) + continue; + pref_changed_map->emplace(item.first, *(pref->GetValue()) != item.second); + } +} + +} // namespace + +PrefService::PrefService( + std::unique_ptr<PrefNotifierImpl> pref_notifier, + std::unique_ptr<PrefValueStore> pref_value_store, + scoped_refptr<PersistentPrefStore> user_prefs, + scoped_refptr<PrefRegistry> pref_registry, + base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)> + read_error_callback, + bool async) + : pref_notifier_(std::move(pref_notifier)), + pref_value_store_(std::move(pref_value_store)), + user_pref_store_(std::move(user_prefs)), + read_error_callback_(std::move(read_error_callback)), + pref_registry_(std::move(pref_registry)) { + pref_notifier_->SetPrefService(this); + + DCHECK(pref_registry_); + DCHECK(pref_value_store_); + + InitFromStorage(async); +} + +PrefService::~PrefService() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // TODO(crbug.com/942491, 946668, 945772) The following code collects + // augments stack dumps created by ~PrefNotifierImpl() with information + // whether the profile owning the PrefService is an incognito profile. + // Delete this, once the bugs are closed. + const bool is_incognito_profile = user_pref_store_->IsInMemoryPrefStore(); + base::debug::Alias(&is_incognito_profile); + // Export value of is_incognito_profile to a string so that `grep` + // is a sufficient tool to analyze crashdumps. + char is_incognito_profile_string[32]; + strncpy(is_incognito_profile_string, + is_incognito_profile ? "is_incognito: yes" : "is_incognito: no", + sizeof(is_incognito_profile_string)); + base::debug::Alias(&is_incognito_profile_string); +} + +void PrefService::InitFromStorage(bool async) { + if (user_pref_store_->IsInitializationComplete()) { + read_error_callback_.Run(user_pref_store_->GetReadError()); + } else if (!async) { + read_error_callback_.Run(user_pref_store_->ReadPrefs()); + } else { + // Guarantee that initialization happens after this function returned. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&PersistentPrefStore::ReadPrefsAsync, user_pref_store_, + new ReadErrorHandler(read_error_callback_))); + } +} + +void PrefService::CommitPendingWrite( + base::OnceClosure reply_callback, + base::OnceClosure synchronous_done_callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + user_pref_store_->CommitPendingWrite(std::move(reply_callback), + std::move(synchronous_done_callback)); +} + +void PrefService::SchedulePendingLossyWrites() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + user_pref_store_->SchedulePendingLossyWrites(); +} + +bool PrefService::GetBoolean(const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + bool result = false; + + const base::Value* value = GetPreferenceValueChecked(path); + if (!value) + return result; + bool rv = value->GetAsBoolean(&result); + DCHECK(rv); + return result; +} + +int PrefService::GetInteger(const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + int result = 0; + + const base::Value* value = GetPreferenceValueChecked(path); + if (!value) + return result; + bool rv = value->GetAsInteger(&result); + DCHECK(rv); + return result; +} + +double PrefService::GetDouble(const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + double result = 0.0; + + const base::Value* value = GetPreferenceValueChecked(path); + if (!value) + return result; + bool rv = value->GetAsDouble(&result); + DCHECK(rv); + return result; +} + +std::string PrefService::GetString(const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + std::string result; + + const base::Value* value = GetPreferenceValueChecked(path); + if (!value) + return result; + bool rv = value->GetAsString(&result); + DCHECK(rv); + return result; +} + +base::FilePath PrefService::GetFilePath(const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + base::FilePath result; + + const base::Value* value = GetPreferenceValueChecked(path); + if (!value) + return base::FilePath(result); + bool rv = base::GetValueAsFilePath(*value, &result); + DCHECK(rv); + return result; +} + +bool PrefService::HasPrefPath(const std::string& path) const { + const Preference* pref = FindPreference(path); + return pref && !pref->IsDefaultValue(); +} + +void PrefService::IteratePreferenceValues( + base::RepeatingCallback<void(const std::string& key, + const base::Value& value)> callback) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (const auto& it : *pref_registry_) + callback.Run(it.first, *GetPreferenceValue(it.first)); +} + +std::unique_ptr<base::DictionaryValue> PrefService::GetPreferenceValues( + IncludeDefaults include_defaults) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::unique_ptr<base::DictionaryValue> out(new base::DictionaryValue); + for (const auto& it : *pref_registry_) { + if (include_defaults == INCLUDE_DEFAULTS) { + out->Set(it.first, GetPreferenceValue(it.first)->CreateDeepCopy()); + } else { + const Preference* pref = FindPreference(it.first); + if (pref->IsDefaultValue()) + continue; + out->Set(it.first, pref->GetValue()->CreateDeepCopy()); + } + } + return out; +} + +const PrefService::Preference* PrefService::FindPreference( + const std::string& pref_name) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + auto it = prefs_map_.find(pref_name); + if (it != prefs_map_.end()) + return &(it->second); + const base::Value* default_value = nullptr; + if (!pref_registry_->defaults()->GetValue(pref_name, &default_value)) + return nullptr; + it = prefs_map_ + .insert(std::make_pair( + pref_name, Preference(this, pref_name, default_value->type()))) + .first; + return &(it->second); +} + +bool PrefService::ReadOnly() const { + return user_pref_store_->ReadOnly(); +} + +PrefService::PrefInitializationStatus PrefService::GetInitializationStatus() + const { + if (!user_pref_store_->IsInitializationComplete()) + return INITIALIZATION_STATUS_WAITING; + + switch (user_pref_store_->GetReadError()) { + case PersistentPrefStore::PREF_READ_ERROR_NONE: + return INITIALIZATION_STATUS_SUCCESS; + case PersistentPrefStore::PREF_READ_ERROR_NO_FILE: + return INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE; + default: + return INITIALIZATION_STATUS_ERROR; + } +} + +PrefService::PrefInitializationStatus +PrefService::GetAllPrefStoresInitializationStatus() const { + if (!pref_value_store_->IsInitializationComplete()) + return INITIALIZATION_STATUS_WAITING; + + return GetInitializationStatus(); +} + +bool PrefService::IsManagedPreference(const std::string& pref_name) const { + const Preference* pref = FindPreference(pref_name); + return pref && pref->IsManaged(); +} + +bool PrefService::IsPreferenceManagedByCustodian( + const std::string& pref_name) const { + const Preference* pref = FindPreference(pref_name); + return pref && pref->IsManagedByCustodian(); +} + +bool PrefService::IsUserModifiablePreference( + const std::string& pref_name) const { + const Preference* pref = FindPreference(pref_name); + return pref && pref->IsUserModifiable(); +} + +const base::Value* PrefService::Get(const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const base::Value* value = GetPreferenceValueChecked(path); + if (!value) + return nullptr; + return value; +} + +const base::DictionaryValue* PrefService::GetDictionary( + const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const base::Value* value = GetPreferenceValueChecked(path); + if (!value) + return nullptr; + if (value->type() != base::Value::Type::DICTIONARY) { + NOTREACHED(); + return nullptr; + } + return static_cast<const base::DictionaryValue*>(value); +} + +const base::Value* PrefService::GetUserPrefValue( + const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to get an unregistered pref: " << path; + return nullptr; + } + + // Look for an existing preference in the user store. If it doesn't + // exist, return NULL. + base::Value* value = nullptr; + if (!user_pref_store_->GetMutableValue(path, &value)) + return nullptr; + + if (value->type() != pref->GetType()) { + NOTREACHED() << "Pref value type doesn't match registered type."; + return nullptr; + } + + return value; +} + +void PrefService::SetDefaultPrefValue(const std::string& path, + base::Value value) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + pref_registry_->SetDefaultPrefValue(path, std::move(value)); +} + +const base::Value* PrefService::GetDefaultPrefValue( + const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Lookup the preference in the default store. + const base::Value* value = nullptr; + bool has_value = pref_registry_->defaults()->GetValue(path, &value); + DCHECK(has_value) << "Default value missing for pref: " << path; + return value; +} + +const base::ListValue* PrefService::GetList(const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const base::Value* value = GetPreferenceValueChecked(path); + if (!value) + return nullptr; + if (value->type() != base::Value::Type::LIST) { + NOTREACHED(); + return nullptr; + } + return static_cast<const base::ListValue*>(value); +} + +void PrefService::AddPrefObserver(const std::string& path, PrefObserver* obs) { + pref_notifier_->AddPrefObserver(path, obs); +} + +void PrefService::RemovePrefObserver(const std::string& path, + PrefObserver* obs) { + pref_notifier_->RemovePrefObserver(path, obs); +} + +void PrefService::AddPrefInitObserver(base::OnceCallback<void(bool)> obs) { + pref_notifier_->AddInitObserver(std::move(obs)); +} + +PrefRegistry* PrefService::DeprecatedGetPrefRegistry() { + return pref_registry_.get(); +} + +void PrefService::ClearPref(const std::string& path) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to clear an unregistered pref: " << path; + return; + } + user_pref_store_->RemoveValue(path, GetWriteFlags(pref)); +} + +void PrefService::ClearMutableValues() { + user_pref_store_->ClearMutableValues(); +} + +void PrefService::OnStoreDeletionFromDisk() { + user_pref_store_->OnStoreDeletionFromDisk(); +} + +void PrefService::ChangePrefValueStore( + PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* recommended_prefs, + std::unique_ptr<PrefValueStore::Delegate> delegate) { + // Only adding new pref stores are supported. + DCHECK(!pref_value_store_->HasPrefStore(PrefValueStore::MANAGED_STORE) || + !managed_prefs); + DCHECK( + !pref_value_store_->HasPrefStore(PrefValueStore::SUPERVISED_USER_STORE) || + !supervised_user_prefs); + DCHECK(!pref_value_store_->HasPrefStore(PrefValueStore::EXTENSION_STORE) || + !extension_prefs); + DCHECK(!pref_value_store_->HasPrefStore(PrefValueStore::RECOMMENDED_STORE) || + !recommended_prefs); + + // If some of the stores are already initialized, check for pref value changes + // according to store precedence. + std::map<std::string, bool> pref_changed_map; + CheckForNewPrefChangesInPrefStore(&pref_changed_map, managed_prefs, this); + CheckForNewPrefChangesInPrefStore(&pref_changed_map, supervised_user_prefs, + this); + CheckForNewPrefChangesInPrefStore(&pref_changed_map, extension_prefs, this); + CheckForNewPrefChangesInPrefStore(&pref_changed_map, recommended_prefs, this); + + pref_value_store_ = pref_value_store_->CloneAndSpecialize( + managed_prefs, supervised_user_prefs, extension_prefs, + nullptr /* command_line_prefs */, nullptr /* user_prefs */, + recommended_prefs, nullptr /* default_prefs */, pref_notifier_.get(), + std::move(delegate)); + + // Notify |pref_notifier_| on all changed values. + for (const auto& kv : pref_changed_map) { + if (kv.second) + pref_notifier_.get()->OnPreferenceChanged(kv.first); + } +} + +void PrefService::AddPrefObserverAllPrefs(PrefObserver* obs) { + pref_notifier_->AddPrefObserverAllPrefs(obs); +} + +void PrefService::RemovePrefObserverAllPrefs(PrefObserver* obs) { + pref_notifier_->RemovePrefObserverAllPrefs(obs); +} + +void PrefService::Set(const std::string& path, const base::Value& value) { + SetUserPrefValue(path, value.CreateDeepCopy()); +} + +void PrefService::SetBoolean(const std::string& path, bool value) { + SetUserPrefValue(path, std::make_unique<base::Value>(value)); +} + +void PrefService::SetInteger(const std::string& path, int value) { + SetUserPrefValue(path, std::make_unique<base::Value>(value)); +} + +void PrefService::SetDouble(const std::string& path, double value) { + SetUserPrefValue(path, std::make_unique<base::Value>(value)); +} + +void PrefService::SetString(const std::string& path, const std::string& value) { + SetUserPrefValue(path, std::make_unique<base::Value>(value)); +} + +void PrefService::SetFilePath(const std::string& path, + const base::FilePath& value) { + SetUserPrefValue( + path, base::Value::ToUniquePtrValue(base::CreateFilePathValue(value))); +} + +void PrefService::SetInt64(const std::string& path, int64_t value) { + SetUserPrefValue(path, + base::Value::ToUniquePtrValue(util::Int64ToValue(value))); +} + +int64_t PrefService::GetInt64(const std::string& path) const { + const base::Value* value = GetPreferenceValueChecked(path); + base::Optional<int64_t> integer = util::ValueToInt64(value); + DCHECK(integer); + return integer.value_or(0); +} + +void PrefService::SetUint64(const std::string& path, uint64_t value) { + SetUserPrefValue(path, + std::make_unique<base::Value>(base::NumberToString(value))); +} + +uint64_t PrefService::GetUint64(const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const base::Value* value = GetPreferenceValueChecked(path); + if (!value) + return 0; + std::string result("0"); + bool rv = value->GetAsString(&result); + DCHECK(rv); + + uint64_t val; + base::StringToUint64(result, &val); + return val; +} + +void PrefService::SetTime(const std::string& path, base::Time value) { + SetUserPrefValue(path, + base::Value::ToUniquePtrValue(util::TimeToValue(value))); +} + +base::Time PrefService::GetTime(const std::string& path) const { + const base::Value* value = GetPreferenceValueChecked(path); + base::Optional<base::Time> time = util::ValueToTime(value); + DCHECK(time); + return time.value_or(base::Time()); +} + +void PrefService::SetTimeDelta(const std::string& path, base::TimeDelta value) { + SetUserPrefValue( + path, base::Value::ToUniquePtrValue(util::TimeDeltaToValue(value))); +} + +base::TimeDelta PrefService::GetTimeDelta(const std::string& path) const { + const base::Value* value = GetPreferenceValueChecked(path); + base::Optional<base::TimeDelta> time_delta = util::ValueToTimeDelta(value); + DCHECK(time_delta); + return time_delta.value_or(base::TimeDelta()); +} + +base::Value* PrefService::GetMutableUserPref(const std::string& path, + base::Value::Type type) { + CHECK(type == base::Value::Type::DICTIONARY || + type == base::Value::Type::LIST); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to get an unregistered pref: " << path; + return nullptr; + } + if (pref->GetType() != type) { + NOTREACHED() << "Wrong type for GetMutableValue: " << path; + return nullptr; + } + + // Look for an existing preference in the user store. Return it in case it + // exists and has the correct type. + base::Value* value = nullptr; + if (user_pref_store_->GetMutableValue(path, &value) && + value->type() == type) { + return value; + } + + // TODO(crbug.com/859477): Remove once root cause has been found. + if (value && value->type() != type) { + DEBUG_ALIAS_FOR_CSTR(path_copy, path.c_str(), 1024); + base::debug::DumpWithoutCrashing(); + } + + // If no user preference of the correct type exists, clone default value. + const base::Value* default_value = nullptr; + pref_registry_->defaults()->GetValue(path, &default_value); + // TODO(crbug.com/859477): Revert to DCHECK once root cause has been found. + if (default_value->type() != type) { + DEBUG_ALIAS_FOR_CSTR(path_copy, path.c_str(), 1024); + base::debug::DumpWithoutCrashing(); + } + user_pref_store_->SetValueSilently(path, default_value->CreateDeepCopy(), + GetWriteFlags(pref)); + user_pref_store_->GetMutableValue(path, &value); + return value; +} + +void PrefService::ReportUserPrefChanged(const std::string& key) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + user_pref_store_->ReportValueChanged(key, GetWriteFlags(FindPreference(key))); +} + +void PrefService::ReportUserPrefChanged( + const std::string& key, + std::set<std::vector<std::string>> path_components) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + user_pref_store_->ReportSubValuesChanged(key, std::move(path_components), + GetWriteFlags(FindPreference(key))); +} + +void PrefService::SetUserPrefValue(const std::string& path, + std::unique_ptr<base::Value> new_value) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->GetType() != new_value->type()) { + NOTREACHED() << "Trying to set pref " << path << " of type " + << pref->GetType() << " to value of type " + << new_value->type(); + return; + } + + user_pref_store_->SetValue(path, std::move(new_value), GetWriteFlags(pref)); +} + +void PrefService::UpdateCommandLinePrefStore(PrefStore* command_line_store) { + pref_value_store_->UpdateCommandLinePrefStore(command_line_store); +} + +/////////////////////////////////////////////////////////////////////////////// +// PrefService::Preference + +PrefService::Preference::Preference(const PrefService* service, + std::string name, + base::Value::Type type) + : name_(std::move(name)), + type_(type), + // Cache the registration flags at creation time to avoid multiple map + // lookups later. + registration_flags_(service->pref_registry_->GetRegistrationFlags(name_)), + pref_service_(service) {} + +const base::Value* PrefService::Preference::GetValue() const { + return pref_service_->GetPreferenceValueChecked(name_); +} + +const base::Value* PrefService::Preference::GetRecommendedValue() const { + DCHECK(pref_service_->FindPreference(name_)) + << "Must register pref before getting its value"; + + const base::Value* found_value = nullptr; + if (pref_value_store()->GetRecommendedValue(name_, type_, &found_value)) { + DCHECK(found_value->type() == type_); + return found_value; + } + + // The pref has no recommended value. + return nullptr; +} + +bool PrefService::Preference::IsManaged() const { + return pref_value_store()->PrefValueInManagedStore(name_); +} + +bool PrefService::Preference::IsManagedByCustodian() const { + return pref_value_store()->PrefValueInSupervisedStore(name_); +} + +bool PrefService::Preference::IsRecommended() const { + return pref_value_store()->PrefValueFromRecommendedStore(name_); +} + +bool PrefService::Preference::HasExtensionSetting() const { + return pref_value_store()->PrefValueInExtensionStore(name_); +} + +bool PrefService::Preference::HasUserSetting() const { + return pref_value_store()->PrefValueInUserStore(name_); +} + +bool PrefService::Preference::IsExtensionControlled() const { + return pref_value_store()->PrefValueFromExtensionStore(name_); +} + +bool PrefService::Preference::IsUserControlled() const { + return pref_value_store()->PrefValueFromUserStore(name_); +} + +bool PrefService::Preference::IsDefaultValue() const { + return pref_value_store()->PrefValueFromDefaultStore(name_); +} + +bool PrefService::Preference::IsUserModifiable() const { + return pref_value_store()->PrefValueUserModifiable(name_); +} + +bool PrefService::Preference::IsExtensionModifiable() const { + return pref_value_store()->PrefValueExtensionModifiable(name_); +} + +const base::Value* PrefService::GetPreferenceValue( + const std::string& path) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // TODO(battre): This is a check for crbug.com/435208. After analyzing some + // crash dumps it looks like the PrefService is accessed even though it has + // been cleared already. + CHECK(pref_registry_); + CHECK(pref_registry_->defaults()); + CHECK(pref_value_store_); + + const base::Value* default_value = nullptr; + if (pref_registry_->defaults()->GetValue(path, &default_value)) { + const base::Value* found_value = nullptr; + base::Value::Type default_type = default_value->type(); + if (pref_value_store_->GetValue(path, default_type, &found_value)) { + DCHECK(found_value->type() == default_type); + return found_value; + } else { + // Every registered preference has at least a default value. + NOTREACHED() << "no valid value found for registered pref " << path; + } + } + + return nullptr; +} + +const base::Value* PrefService::GetPreferenceValueChecked( + const std::string& path) const { + const base::Value* value = GetPreferenceValue(path); + DCHECK(value) << "Trying to read an unregistered pref: " << path; + return value; +}
diff --git a/src/updater_components/prefs/pref_service.h b/src/updater_components/prefs/pref_service.h new file mode 100644 index 0000000..9580b48 --- /dev/null +++ b/src/updater_components/prefs/pref_service.h
@@ -0,0 +1,468 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This provides a way to access the application's current preferences. + +// Chromium settings and storage represent user-selected preferences and +// information and MUST not be extracted, overwritten or modified except +// through Chromium defined APIs. + +#ifndef COMPONENTS_PREFS_PREF_SERVICE_H_ +#define COMPONENTS_PREFS_PREF_SERVICE_H_ + +#include <stdint.h> + +#include <memory> +#include <set> +#include <string> +#include <unordered_map> +#include <vector> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "base/sequence_checker.h" +#include "base/time/time.h" +#include "base/values.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/prefs/pref_value_store.h" +#include "components/prefs/prefs_export.h" + +class PrefNotifier; +class PrefNotifierImpl; +class PrefObserver; +class PrefRegistry; +class PrefStore; + +namespace base { +class FilePath; +} + +namespace prefs { +class ScopedDictionaryPrefUpdate; +} + +namespace subtle { +class PrefMemberBase; +class ScopedUserPrefUpdateBase; +} + +// Base class for PrefServices. You can use the base class to read and +// interact with preferences, but not to register new preferences; for +// that see e.g. PrefRegistrySimple. +// +// Settings and storage accessed through this class represent +// user-selected preferences and information and MUST not be +// extracted, overwritten or modified except through the defined APIs. +class COMPONENTS_PREFS_EXPORT PrefService { + public: + enum PrefInitializationStatus { + INITIALIZATION_STATUS_WAITING, + INITIALIZATION_STATUS_SUCCESS, + INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE, + INITIALIZATION_STATUS_ERROR + }; + + enum IncludeDefaults { + INCLUDE_DEFAULTS, + EXCLUDE_DEFAULTS, + }; + + // A helper class to store all the information associated with a preference. + class COMPONENTS_PREFS_EXPORT Preference { + public: + // The type of the preference is determined by the type with which it is + // registered. This type needs to be a boolean, integer, double, string, + // dictionary (a branch), or list. You shouldn't need to construct this on + // your own; use the PrefService::Register*Pref methods instead. + Preference(const PrefService* service, + std::string name, + base::Value::Type type); + ~Preference() {} + + // Returns the name of the Preference (i.e., the key, e.g., + // browser.window_placement). + std::string name() const { return name_; } + + // Returns the registered type of the preference. + base::Value::Type GetType() const { return type_; } + + // Returns the value of the Preference, falling back to the registered + // default value if no other has been set. + const base::Value* GetValue() const; + + // Returns the value recommended by the admin, if any. + const base::Value* GetRecommendedValue() const; + + // Returns true if the Preference is managed, i.e. set by an admin policy. + // Since managed prefs have the highest priority, this also indicates + // whether the pref is actually being controlled by the policy setting. + bool IsManaged() const; + + // Returns true if the Preference is controlled by the custodian of the + // supervised user. Since a supervised user is not expected to have an admin + // policy, this is the controlling pref if set. + bool IsManagedByCustodian() const; + + // Returns true if the Preference's current value is one recommended by + // admin policy. Note that this will be false if any other higher-priority + // source overrides the value (e.g., the user has set a value). + bool IsRecommended() const; + + // Returns true if the Preference has a value set by an extension, even if + // that value is being overridden by a higher-priority source. + bool HasExtensionSetting() const; + + // Returns true if the Preference has a user setting, even if that value is + // being overridden by a higher-priority source. + bool HasUserSetting() const; + + // Returns true if the Preference value is currently being controlled by an + // extension, and not by any higher-priority source. + bool IsExtensionControlled() const; + + // Returns true if the Preference value is currently being controlled by a + // user setting, and not by any higher-priority source. + bool IsUserControlled() const; + + // Returns true if the Preference is currently using its default value, + // and has not been set by any higher-priority source (even with the same + // value). + bool IsDefaultValue() const; + + // Returns true if the user can change the Preference value, which is the + // case if no higher-priority source than the user store controls the + // Preference. + bool IsUserModifiable() const; + + // Returns true if an extension can change the Preference value, which is + // the case if no higher-priority source than the extension store controls + // the Preference. + bool IsExtensionModifiable() const; + + // Return the registration flags for this pref as a bitmask of + // PrefRegistry::PrefRegistrationFlags. + uint32_t registration_flags() const { return registration_flags_; } + + private: + friend class PrefService; + + PrefValueStore* pref_value_store() const { + return pref_service_->pref_value_store_.get(); + } + + const std::string name_; + + const base::Value::Type type_; + + const uint32_t registration_flags_; + + // Reference to the PrefService in which this pref was created. + const PrefService* const pref_service_; + }; + + // You may wish to use PrefServiceFactory or one of its subclasses + // for simplified construction. + PrefService(std::unique_ptr<PrefNotifierImpl> pref_notifier, + std::unique_ptr<PrefValueStore> pref_value_store, + scoped_refptr<PersistentPrefStore> user_prefs, + scoped_refptr<PrefRegistry> pref_registry, + base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)> + read_error_callback, + bool async); + virtual ~PrefService(); + + // Lands pending writes to disk. This should only be used if we need to save + // immediately (basically, during shutdown). |reply_callback| will be posted + // to the current sequence when changes have been written. + // |synchronous_done_callback| on the other hand will be invoked right away + // wherever the writes complete (could even be invoked synchronously if no + // writes need to occur); this is useful when the current thread cannot pump + // messages to observe the reply (e.g. nested loops banned on main thread + // during shutdown). |synchronous_done_callback| must be thread-safe. + void CommitPendingWrite( + base::OnceClosure reply_callback = base::OnceClosure(), + base::OnceClosure synchronous_done_callback = base::OnceClosure()); + + // Schedule a write if there is any lossy data pending. Unlike + // CommitPendingWrite() this does not immediately sync to disk, instead it + // triggers an eventual write if there is lossy data pending and if there + // isn't one scheduled already. + void SchedulePendingLossyWrites(); + + // Returns true if the preference for the given preference name is available + // and is managed. + bool IsManagedPreference(const std::string& pref_name) const; + + // Returns true if the preference for the given preference name is available + // and is controlled by the parent/guardian of the child Account. + bool IsPreferenceManagedByCustodian(const std::string& pref_name) const; + + // Returns |true| if a preference with the given name is available and its + // value can be changed by the user. + bool IsUserModifiablePreference(const std::string& pref_name) const; + + // Look up a preference. Returns NULL if the preference is not + // registered. + const PrefService::Preference* FindPreference(const std::string& path) const; + + // If the path is valid and the value at the end of the path matches the type + // specified, it will return the specified value. Otherwise, the default + // value (set when the pref was registered) will be returned. + bool GetBoolean(const std::string& path) const; + int GetInteger(const std::string& path) const; + double GetDouble(const std::string& path) const; + std::string GetString(const std::string& path) const; + base::FilePath GetFilePath(const std::string& path) const; + + // Returns the branch if it exists, or the registered default value otherwise. + // Note that |path| must point to a registered preference. In that case, these + // functions will never return NULL. + const base::Value* Get(const std::string& path) const; + const base::DictionaryValue* GetDictionary(const std::string& path) const; + const base::ListValue* GetList(const std::string& path) const; + + // Removes a user pref and restores the pref to its default value. + void ClearPref(const std::string& path); + + // If the path is valid (i.e., registered), update the pref value in the user + // prefs. + // To set the value of dictionary or list values in the pref tree use + // Set(), but to modify the value of a dictionary or list use either + // ListPrefUpdate or DictionaryPrefUpdate from scoped_user_pref_update.h. + void Set(const std::string& path, const base::Value& value); + void SetBoolean(const std::string& path, bool value); + void SetInteger(const std::string& path, int value); + void SetDouble(const std::string& path, double value); + void SetString(const std::string& path, const std::string& value); + void SetFilePath(const std::string& path, const base::FilePath& value); + + // Int64 helper methods that actually store the given value as a string. + // Note that if obtaining the named value via GetDictionary or GetList, the + // Value type will be Type::STRING. + void SetInt64(const std::string& path, int64_t value); + int64_t GetInt64(const std::string& path) const; + + // As above, but for unsigned values. + void SetUint64(const std::string& path, uint64_t value); + uint64_t GetUint64(const std::string& path) const; + + // Time helper methods that actually store the given value as a string, which + // represents the number of microseconds elapsed (absolute for TimeDelta and + // relative to Windows epoch for Time variants). Note that if obtaining the + // named value via GetDictionary or GetList, the Value type will be + // Type::STRING. + void SetTime(const std::string& path, base::Time value); + base::Time GetTime(const std::string& path) const; + void SetTimeDelta(const std::string& path, base::TimeDelta value); + base::TimeDelta GetTimeDelta(const std::string& path) const; + + // Returns the value of the given preference, from the user pref store. If + // the preference is not set in the user pref store, returns NULL. + const base::Value* GetUserPrefValue(const std::string& path) const; + + // Changes the default value for a preference. + // + // Will cause a pref change notification to be fired if this causes + // the effective value to change. + void SetDefaultPrefValue(const std::string& path, base::Value value); + + // Returns the default value of the given preference. |path| must point to a + // registered preference. In that case, will never return nullptr, so callers + // do not need to check this. + const base::Value* GetDefaultPrefValue(const std::string& path) const; + + // Returns true if a value has been set for the specified path. + // NOTE: this is NOT the same as FindPreference. In particular + // FindPreference returns whether RegisterXXX has been invoked, where as + // this checks if a value exists for the path. + bool HasPrefPath(const std::string& path) const; + + // Issues a callback for every preference value. The preferences must not be + // mutated during iteration. + void IteratePreferenceValues( + base::RepeatingCallback<void(const std::string& key, + const base::Value& value)> callback) const; + + // Returns a dictionary with effective preference values. This is an expensive + // operation which does a deep copy. Use only if you really need the results + // in a base::Value (for example, for JSON serialization). Otherwise use + // IteratePreferenceValues above to avoid the copies. + // + // If INCLUDE_DEFAULTS is requested, preferences set to their default values + // will be included. Otherwise, these will be omitted from the returned + // dictionary. + std::unique_ptr<base::DictionaryValue> GetPreferenceValues( + IncludeDefaults include_defaults) const; + + bool ReadOnly() const; + + // Returns the initialization state, taking only user prefs into account. + PrefInitializationStatus GetInitializationStatus() const; + + // Returns the initialization state, taking all pref stores into account. + PrefInitializationStatus GetAllPrefStoresInitializationStatus() const; + + // Tell our PrefValueStore to update itself to |command_line_store|. + // Takes ownership of the store. + virtual void UpdateCommandLinePrefStore(PrefStore* command_line_store); + + // We run the callback once, when initialization completes. The bool + // parameter will be set to true for successful initialization, + // false for unsuccessful. + void AddPrefInitObserver(base::OnceCallback<void(bool)> callback); + + // Returns the PrefRegistry object for this service. You should not + // use this; the intent is for no registrations to take place after + // PrefService has been constructed. + // + // Instead of using this method, the recommended approach is to + // register all preferences for a class Xyz up front in a static + // Xyz::RegisterPrefs function, which gets invoked early in the + // application's start-up, before a PrefService is created. + // + // As an example, prefs registration in Chrome is triggered by the + // functions chrome::RegisterPrefs (for global preferences) and + // chrome::RegisterProfilePrefs (for user-specific preferences) + // implemented in chrome/browser/prefs/browser_prefs.cc. + PrefRegistry* DeprecatedGetPrefRegistry(); + + // Clears mutable values. + void ClearMutableValues(); + + // Invoked when the store is deleted from disk. Allows this PrefService + // to tangentially cleanup data it may have saved outside the store. + void OnStoreDeletionFromDisk(); + + // Add new pref stores to the existing PrefValueStore. Only adding new + // stores are allowed. If a corresponding store already exists, calling this + // will cause DCHECK failures. If the newly added stores already contain + // values, PrefNotifier associated with this object will be notified with + // these values. |delegate| can be passed to observe events of the new + // PrefValueStore. + // TODO(qinmin): packaging all the input params in a struct, and do the same + // for the constructor. + void ChangePrefValueStore( + PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* recommended_prefs, + std::unique_ptr<PrefValueStore::Delegate> delegate = nullptr); + + // A low level function for registering an observer for every single + // preference changed notification. The caller must ensure that the observer + // remains valid as long as it is registered. Pointer ownership is not + // transferred. + // + // Almost all calling code should use a PrefChangeRegistrar instead. + // + // AVOID ADDING THESE. These are low-level observer notifications that are + // called for every pref change. This can lead to inefficiency, and the lack + // of a "registrar" model makes it easy to forget to undregister. It is + // really designed for integrating other notification systems, not for normal + // observation. + void AddPrefObserverAllPrefs(PrefObserver* obs); + void RemovePrefObserverAllPrefs(PrefObserver* obs); + + protected: + // The PrefNotifier handles registering and notifying preference observers. + // It is created and owned by this PrefService. Subclasses may access it for + // unit testing. + const std::unique_ptr<PrefNotifierImpl> pref_notifier_; + + // The PrefValueStore provides prioritized preference values. It is owned by + // this PrefService. Subclasses may access it for unit testing. + std::unique_ptr<PrefValueStore> pref_value_store_; + + // Pref Stores and profile that we passed to the PrefValueStore. + const scoped_refptr<PersistentPrefStore> user_pref_store_; + + // Callback to call when a read error occurs. Always invoked on the sequence + // this PrefService was created own. + const base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)> + read_error_callback_; + + private: + // Hash map expected to be fastest here since it minimises expensive + // string comparisons. Order is unimportant, and deletions are rare. + // Confirmed on Android where this speeded Chrome startup by roughly 50ms + // vs. std::map, and by roughly 180ms vs. std::set of Preference pointers. + typedef std::unordered_map<std::string, Preference> PreferenceMap; + + // Give access to ReportUserPrefChanged() and GetMutableUserPref(). + friend class subtle::ScopedUserPrefUpdateBase; + friend class PrefServiceTest_WriteablePrefStoreFlags_Test; + friend class prefs::ScopedDictionaryPrefUpdate; + + // Registration of pref change observers must be done using the + // PrefChangeRegistrar, which is declared as a friend here to grant it + // access to the otherwise protected members Add/RemovePrefObserver. + // PrefMember registers for preferences changes notification directly to + // avoid the storage overhead of the registrar, so its base class must be + // declared as a friend, too. + friend class PrefChangeRegistrar; + friend class subtle::PrefMemberBase; + + // These are protected so they can only be accessed by the friend + // classes listed above. + // + // If the pref at the given path changes, we call the observer's + // OnPreferenceChanged method. Note that observers should not call + // these methods directly but rather use a PrefChangeRegistrar to + // make sure the observer gets cleaned up properly. + // + // Virtual for testing. + virtual void AddPrefObserver(const std::string& path, PrefObserver* obs); + virtual void RemovePrefObserver(const std::string& path, PrefObserver* obs); + + // Sends notification of a changed preference. This needs to be called by + // a ScopedUserPrefUpdate or ScopedDictionaryPrefUpdate if a DictionaryValue + // or ListValue is changed. + void ReportUserPrefChanged(const std::string& key); + void ReportUserPrefChanged( + const std::string& key, + std::set<std::vector<std::string>> path_components); + + // Sets the value for this pref path in the user pref store and informs the + // PrefNotifier of the change. + void SetUserPrefValue(const std::string& path, + std::unique_ptr<base::Value> new_value); + + // Load preferences from storage, attempting to diagnose and handle errors. + // This should only be called from the constructor. + void InitFromStorage(bool async); + + // Used to set the value of dictionary or list values in the user pref store. + // This will create a dictionary or list if one does not exist in the user + // pref store. This method returns NULL only if you're requesting an + // unregistered pref or a non-dict/non-list pref. + // |type| may only be Values::Type::DICTIONARY or Values::Type::LIST and + // |path| must point to a registered preference of type |type|. + // Ownership of the returned value remains at the user pref store. + base::Value* GetMutableUserPref(const std::string& path, + base::Value::Type type); + + // GetPreferenceValue is the equivalent of FindPreference(path)->GetValue(), + // it has been added for performance. It is faster because it does + // not need to find or create a Preference object to get the + // value (GetValue() calls back though the preference service to + // actually get the value.). + const base::Value* GetPreferenceValue(const std::string& path) const; + const base::Value* GetPreferenceValueChecked(const std::string& path) const; + + const scoped_refptr<PrefRegistry> pref_registry_; + + // Local cache of registered Preference objects. The pref_registry_ + // is authoritative with respect to what the types and default values + // of registered preferences are. + mutable PreferenceMap prefs_map_; + + SEQUENCE_CHECKER(sequence_checker_); + + DISALLOW_COPY_AND_ASSIGN(PrefService); +}; + +#endif // COMPONENTS_PREFS_PREF_SERVICE_H_
diff --git a/src/updater_components/prefs/pref_service_factory.cc b/src/updater_components/prefs/pref_service_factory.cc new file mode 100644 index 0000000..cc8e52d --- /dev/null +++ b/src/updater_components/prefs/pref_service_factory.cc
@@ -0,0 +1,51 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_service_factory.h" + +#include <memory> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/sequenced_task_runner.h" +#include "components/prefs/default_pref_store.h" +#include "components/prefs/json_pref_store.h" +#include "components/prefs/pref_filter.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/pref_value_store.h" + +PrefServiceFactory::PrefServiceFactory() + : read_error_callback_(base::DoNothing()), async_(false) {} + +PrefServiceFactory::~PrefServiceFactory() {} + +void PrefServiceFactory::SetUserPrefsFile( + const base::FilePath& prefs_file, + base::SequencedTaskRunner* task_runner) { + user_prefs_ = + base::MakeRefCounted<JsonPrefStore>(prefs_file, nullptr, task_runner); +} + +std::unique_ptr<PrefService> PrefServiceFactory::Create( + scoped_refptr<PrefRegistry> pref_registry, + std::unique_ptr<PrefValueStore::Delegate> delegate) { + auto pref_notifier = std::make_unique<PrefNotifierImpl>(); + auto pref_value_store = std::make_unique<PrefValueStore>( + managed_prefs_.get(), supervised_user_prefs_.get(), + extension_prefs_.get(), command_line_prefs_.get(), user_prefs_.get(), + recommended_prefs_.get(), pref_registry->defaults().get(), + pref_notifier.get(), std::move(delegate)); + return std::make_unique<PrefService>( + std::move(pref_notifier), std::move(pref_value_store), user_prefs_.get(), + std::move(pref_registry), read_error_callback_, async_); +} + +void PrefServiceFactory::ChangePrefValueStore( + PrefService* pref_service, + std::unique_ptr<PrefValueStore::Delegate> delegate) { + pref_service->ChangePrefValueStore( + managed_prefs_.get(), supervised_user_prefs_.get(), + extension_prefs_.get(), recommended_prefs_.get(), std::move(delegate)); +}
diff --git a/src/updater_components/prefs/pref_service_factory.h b/src/updater_components/prefs/pref_service_factory.h new file mode 100644 index 0000000..4e7d922 --- /dev/null +++ b/src/updater_components/prefs/pref_service_factory.h
@@ -0,0 +1,101 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_ +#define COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/prefs/pref_registry.h" +#include "components/prefs/pref_store.h" +#include "components/prefs/pref_value_store.h" +#include "components/prefs/prefs_export.h" + +class PrefService; + +namespace base { +class FilePath; +class SequencedTaskRunner; +} + +// A class that allows convenient building of PrefService. +class COMPONENTS_PREFS_EXPORT PrefServiceFactory { + public: + PrefServiceFactory(); + virtual ~PrefServiceFactory(); + + // Functions for setting the various parameters of the PrefService to build. + void set_managed_prefs(scoped_refptr<PrefStore> prefs) { + managed_prefs_.swap(prefs); + } + + void set_supervised_user_prefs(scoped_refptr<PrefStore> prefs) { + supervised_user_prefs_.swap(prefs); + } + + void set_extension_prefs(scoped_refptr<PrefStore> prefs) { + extension_prefs_.swap(prefs); + } + + void set_command_line_prefs(scoped_refptr<PrefStore> prefs) { + command_line_prefs_.swap(prefs); + } + + void set_user_prefs(scoped_refptr<PersistentPrefStore> prefs) { + user_prefs_.swap(prefs); + } + + void set_recommended_prefs(scoped_refptr<PrefStore> prefs) { + recommended_prefs_.swap(prefs); + } + + // Sets up error callback for the PrefService. A do-nothing default is + // provided if this is not called. This callback is always invoked (async or + // not) on the sequence on which Create is invoked. + void set_read_error_callback( + base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)> + read_error_callback) { + read_error_callback_ = std::move(read_error_callback); + } + + // Specifies to use an actual file-backed user pref store. + void SetUserPrefsFile(const base::FilePath& prefs_file, + base::SequencedTaskRunner* task_runner); + + void set_async(bool async) { + async_ = async; + } + + // Creates a PrefService object initialized with the parameters from + // this factory. + std::unique_ptr<PrefService> Create( + scoped_refptr<PrefRegistry> pref_registry, + std::unique_ptr<PrefValueStore::Delegate> delegate = nullptr); + + // Add pref stores from this object to the |pref_service|. + void ChangePrefValueStore( + PrefService* pref_service, + std::unique_ptr<PrefValueStore::Delegate> delegate = nullptr); + + protected: + scoped_refptr<PrefStore> managed_prefs_; + scoped_refptr<PrefStore> supervised_user_prefs_; + scoped_refptr<PrefStore> extension_prefs_; + scoped_refptr<PrefStore> command_line_prefs_; + scoped_refptr<PersistentPrefStore> user_prefs_; + scoped_refptr<PrefStore> recommended_prefs_; + + base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)> + read_error_callback_; + + // Defaults to false. + bool async_; + + private: + DISALLOW_COPY_AND_ASSIGN(PrefServiceFactory); +}; + +#endif // COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_
diff --git a/src/updater_components/prefs/pref_service_unittest.cc b/src/updater_components/prefs/pref_service_unittest.cc new file mode 100644 index 0000000..3bdcf35 --- /dev/null +++ b/src/updater_components/prefs/pref_service_unittest.cc
@@ -0,0 +1,624 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> +#include <stdint.h> + +#include <string> + +#include "base/bind_helpers.h" +#include "base/stl_util.h" +#include "base/time/time.h" +#include "base/values.h" +#include "components/prefs/json_pref_store.h" +#include "components/prefs/mock_pref_change_callback.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service_factory.h" +#include "components/prefs/pref_value_store.h" +#include "components/prefs/testing_pref_service.h" +#include "components/prefs/testing_pref_store.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Mock; + +namespace { + +const char kPrefName[] = "pref.name"; +const char kManagedPref[] = "managed_pref"; +const char kRecommendedPref[] = "recommended_pref"; +const char kSupervisedPref[] = "supervised_pref"; + +} // namespace + +TEST(PrefServiceTest, NoObserverFire) { + TestingPrefServiceSimple prefs; + + const char pref_name[] = "homepage"; + prefs.registry()->RegisterStringPref(pref_name, std::string()); + + const char new_pref_value[] = "http://www.google.com/"; + MockPrefChangeCallback obs(&prefs); + PrefChangeRegistrar registrar; + registrar.Init(&prefs); + registrar.Add(pref_name, obs.GetCallback()); + + // This should fire the checks in MockPrefChangeCallback::OnPreferenceChanged. + const base::Value expected_value(new_pref_value); + obs.Expect(pref_name, &expected_value); + prefs.SetString(pref_name, new_pref_value); + Mock::VerifyAndClearExpectations(&obs); + + // Setting the pref to the same value should not set the pref value a second + // time. + EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0); + prefs.SetString(pref_name, new_pref_value); + Mock::VerifyAndClearExpectations(&obs); + + // Clearing the pref should cause the pref to fire. + const base::Value expected_default_value((std::string())); + obs.Expect(pref_name, &expected_default_value); + prefs.ClearPref(pref_name); + Mock::VerifyAndClearExpectations(&obs); + + // Clearing the pref again should not cause the pref to fire. + EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0); + prefs.ClearPref(pref_name); + Mock::VerifyAndClearExpectations(&obs); +} + +TEST(PrefServiceTest, HasPrefPath) { + TestingPrefServiceSimple prefs; + + const char path[] = "fake.path"; + + // Shouldn't initially have a path. + EXPECT_FALSE(prefs.HasPrefPath(path)); + + // Register the path. This doesn't set a value, so the path still shouldn't + // exist. + prefs.registry()->RegisterStringPref(path, std::string()); + EXPECT_FALSE(prefs.HasPrefPath(path)); + + // Set a value and make sure we have a path. + prefs.SetString(path, "blah"); + EXPECT_TRUE(prefs.HasPrefPath(path)); +} + +TEST(PrefServiceTest, Observers) { + const char pref_name[] = "homepage"; + + TestingPrefServiceSimple prefs; + prefs.SetUserPref(pref_name, + std::make_unique<base::Value>("http://www.cnn.com")); + prefs.registry()->RegisterStringPref(pref_name, std::string()); + + const char new_pref_value[] = "http://www.google.com/"; + const base::Value expected_new_pref_value(new_pref_value); + MockPrefChangeCallback obs(&prefs); + PrefChangeRegistrar registrar; + registrar.Init(&prefs); + registrar.Add(pref_name, obs.GetCallback()); + + PrefChangeRegistrar registrar_two; + registrar_two.Init(&prefs); + + // This should fire the checks in MockPrefChangeCallback::OnPreferenceChanged. + obs.Expect(pref_name, &expected_new_pref_value); + prefs.SetString(pref_name, new_pref_value); + Mock::VerifyAndClearExpectations(&obs); + + // Now try adding a second pref observer. + const char new_pref_value2[] = "http://www.youtube.com/"; + const base::Value expected_new_pref_value2(new_pref_value2); + MockPrefChangeCallback obs2(&prefs); + obs.Expect(pref_name, &expected_new_pref_value2); + obs2.Expect(pref_name, &expected_new_pref_value2); + registrar_two.Add(pref_name, obs2.GetCallback()); + // This should fire the checks in obs and obs2. + prefs.SetString(pref_name, new_pref_value2); + Mock::VerifyAndClearExpectations(&obs); + Mock::VerifyAndClearExpectations(&obs2); + + // Set a recommended value. + const base::Value recommended_pref_value("http://www.gmail.com/"); + obs.Expect(pref_name, &expected_new_pref_value2); + obs2.Expect(pref_name, &expected_new_pref_value2); + // This should fire the checks in obs and obs2 but with an unchanged value + // as the recommended value is being overridden by the user-set value. + prefs.SetRecommendedPref(pref_name, recommended_pref_value.CreateDeepCopy()); + Mock::VerifyAndClearExpectations(&obs); + Mock::VerifyAndClearExpectations(&obs2); + + // Make sure obs2 still works after removing obs. + registrar.Remove(pref_name); + EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0); + obs2.Expect(pref_name, &expected_new_pref_value); + // This should only fire the observer in obs2. + prefs.SetString(pref_name, new_pref_value); + Mock::VerifyAndClearExpectations(&obs); + Mock::VerifyAndClearExpectations(&obs2); +} + +// Make sure that if a preference changes type, so the wrong type is stored in +// the user pref file, it uses the correct fallback value instead. +TEST(PrefServiceTest, GetValueChangedType) { + const int kTestValue = 10; + TestingPrefServiceSimple prefs; + prefs.registry()->RegisterIntegerPref(kPrefName, kTestValue); + + // Check falling back to a recommended value. + prefs.SetUserPref(kPrefName, std::make_unique<base::Value>("not an integer")); + const PrefService::Preference* pref = prefs.FindPreference(kPrefName); + ASSERT_TRUE(pref); + const base::Value* value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::Type::INTEGER, value->type()); + int actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kTestValue, actual_int_value); +} + +TEST(PrefServiceTest, GetValueAndGetRecommendedValue) { + const int kDefaultValue = 5; + const int kUserValue = 10; + const int kRecommendedValue = 15; + TestingPrefServiceSimple prefs; + prefs.registry()->RegisterIntegerPref(kPrefName, kDefaultValue); + + // Create pref with a default value only. + const PrefService::Preference* pref = prefs.FindPreference(kPrefName); + ASSERT_TRUE(pref); + + // Check that GetValue() returns the default value. + const base::Value* value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::Type::INTEGER, value->type()); + int actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kDefaultValue, actual_int_value); + + // Check that GetRecommendedValue() returns no value. + value = pref->GetRecommendedValue(); + ASSERT_FALSE(value); + + // Set a user-set value. + prefs.SetUserPref(kPrefName, std::make_unique<base::Value>(kUserValue)); + + // Check that GetValue() returns the user-set value. + value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::Type::INTEGER, value->type()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kUserValue, actual_int_value); + + // Check that GetRecommendedValue() returns no value. + value = pref->GetRecommendedValue(); + ASSERT_FALSE(value); + + // Set a recommended value. + prefs.SetRecommendedPref(kPrefName, + std::make_unique<base::Value>(kRecommendedValue)); + + // Check that GetValue() returns the user-set value. + value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::Type::INTEGER, value->type()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kUserValue, actual_int_value); + + // Check that GetRecommendedValue() returns the recommended value. + value = pref->GetRecommendedValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::Type::INTEGER, value->type()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kRecommendedValue, actual_int_value); + + // Remove the user-set value. + prefs.RemoveUserPref(kPrefName); + + // Check that GetValue() returns the recommended value. + value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::Type::INTEGER, value->type()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kRecommendedValue, actual_int_value); + + // Check that GetRecommendedValue() returns the recommended value. + value = pref->GetRecommendedValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::Type::INTEGER, value->type()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kRecommendedValue, actual_int_value); +} + +TEST(PrefServiceTest, SetTimeValue_RegularTime) { + TestingPrefServiceSimple prefs; + + // Register a null time as the default. + prefs.registry()->RegisterTimePref(kPrefName, base::Time()); + EXPECT_TRUE(prefs.GetTime(kPrefName).is_null()); + + // Set a time and make sure that we can read it without any loss of precision. + const base::Time time = base::Time::Now(); + prefs.SetTime(kPrefName, time); + EXPECT_EQ(time, prefs.GetTime(kPrefName)); +} + +TEST(PrefServiceTest, SetTimeValue_NullTime) { + TestingPrefServiceSimple prefs; + + // Register a non-null time as the default. + const base::Time default_time = base::Time::FromDeltaSinceWindowsEpoch( + base::TimeDelta::FromMicroseconds(12345)); + prefs.registry()->RegisterTimePref(kPrefName, default_time); + EXPECT_FALSE(prefs.GetTime(kPrefName).is_null()); + + // Set a null time and make sure that it remains null upon deserialization. + prefs.SetTime(kPrefName, base::Time()); + EXPECT_TRUE(prefs.GetTime(kPrefName).is_null()); +} + +TEST(PrefServiceTest, SetTimeDeltaValue_RegularTimeDelta) { + TestingPrefServiceSimple prefs; + + // Register a zero time delta as the default. + prefs.registry()->RegisterTimeDeltaPref(kPrefName, base::TimeDelta()); + EXPECT_TRUE(prefs.GetTimeDelta(kPrefName).is_zero()); + + // Set a time delta and make sure that we can read it without any loss of + // precision. + const base::TimeDelta delta = base::Time::Now() - base::Time(); + prefs.SetTimeDelta(kPrefName, delta); + EXPECT_EQ(delta, prefs.GetTimeDelta(kPrefName)); +} + +TEST(PrefServiceTest, SetTimeDeltaValue_ZeroTimeDelta) { + TestingPrefServiceSimple prefs; + + // Register a non-zero time delta as the default. + const base::TimeDelta default_delta = + base::TimeDelta::FromMicroseconds(12345); + prefs.registry()->RegisterTimeDeltaPref(kPrefName, default_delta); + EXPECT_FALSE(prefs.GetTimeDelta(kPrefName).is_zero()); + + // Set a zero time delta and make sure that it remains zero upon + // deserialization. + prefs.SetTimeDelta(kPrefName, base::TimeDelta()); + EXPECT_TRUE(prefs.GetTimeDelta(kPrefName).is_zero()); +} + +// A PrefStore which just stores the last write flags that were used to write +// values to it. +class WriteFlagChecker : public TestingPrefStore { + public: + WriteFlagChecker() {} + + void ReportValueChanged(const std::string& key, uint32_t flags) override { + SetLastWriteFlags(flags); + } + + void SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override { + SetLastWriteFlags(flags); + } + + void SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override { + SetLastWriteFlags(flags); + } + + void RemoveValue(const std::string& key, uint32_t flags) override { + SetLastWriteFlags(flags); + } + + uint32_t GetLastFlagsAndClear() { + CHECK(last_write_flags_set_); + uint32_t result = last_write_flags_; + last_write_flags_set_ = false; + last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS; + return result; + } + + bool last_write_flags_set() { return last_write_flags_set_; } + + private: + ~WriteFlagChecker() override {} + + void SetLastWriteFlags(uint32_t flags) { + CHECK(!last_write_flags_set_); + last_write_flags_set_ = true; + last_write_flags_ = flags; + } + + bool last_write_flags_set_ = false; + uint32_t last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS; +}; + +TEST(PrefServiceTest, WriteablePrefStoreFlags) { + scoped_refptr<WriteFlagChecker> flag_checker(new WriteFlagChecker); + scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple); + PrefServiceFactory factory; + factory.set_user_prefs(flag_checker); + std::unique_ptr<PrefService> prefs(factory.Create(registry.get())); + + // The first 8 bits of write flags are reserved for subclasses. Create a + // custom flag in this range + uint32_t kCustomRegistrationFlag = 1 << 2; + + // A map of the registration flags that will be tested and the write flags + // they are expected to convert to. + struct RegistrationToWriteFlags { + const char* pref_name; + uint32_t registration_flags; + uint32_t write_flags; + }; + const RegistrationToWriteFlags kRegistrationToWriteFlags[] = { + {"none", + PrefRegistry::NO_REGISTRATION_FLAGS, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS}, + {"lossy", + PrefRegistry::LOSSY_PREF, + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG}, + {"custom", + kCustomRegistrationFlag, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS}, + {"lossyandcustom", + PrefRegistry::LOSSY_PREF | kCustomRegistrationFlag, + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG}}; + + for (size_t i = 0; i < base::size(kRegistrationToWriteFlags); ++i) { + RegistrationToWriteFlags entry = kRegistrationToWriteFlags[i]; + registry->RegisterDictionaryPref(entry.pref_name, + entry.registration_flags); + + SCOPED_TRACE("Currently testing pref with name: " + + std::string(entry.pref_name)); + + prefs->GetMutableUserPref(entry.pref_name, base::Value::Type::DICTIONARY); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + + prefs->ReportUserPrefChanged(entry.pref_name); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + + prefs->ClearPref(entry.pref_name); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + + prefs->SetUserPrefValue(entry.pref_name, + std::make_unique<base::DictionaryValue>()); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + } +} + +class PrefServiceSetValueTest : public testing::Test { + protected: + static const char kName[]; + static const char kValue[]; + + PrefServiceSetValueTest() : observer_(&prefs_) {} + + TestingPrefServiceSimple prefs_; + MockPrefChangeCallback observer_; +}; + +const char PrefServiceSetValueTest::kName[] = "name"; +const char PrefServiceSetValueTest::kValue[] = "value"; + +TEST_F(PrefServiceSetValueTest, SetStringValue) { + const char default_string[] = "default"; + const base::Value default_value(default_string); + prefs_.registry()->RegisterStringPref(kName, default_string); + + PrefChangeRegistrar registrar; + registrar.Init(&prefs_); + registrar.Add(kName, observer_.GetCallback()); + + // Changing the controlling store from default to user triggers notification. + observer_.Expect(kName, &default_value); + prefs_.Set(kName, default_value); + Mock::VerifyAndClearExpectations(&observer_); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.Set(kName, default_value); + Mock::VerifyAndClearExpectations(&observer_); + + base::Value new_value(kValue); + observer_.Expect(kName, &new_value); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); +} + +TEST_F(PrefServiceSetValueTest, SetDictionaryValue) { + prefs_.registry()->RegisterDictionaryPref(kName); + PrefChangeRegistrar registrar; + registrar.Init(&prefs_); + registrar.Add(kName, observer_.GetCallback()); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.RemoveUserPref(kName); + Mock::VerifyAndClearExpectations(&observer_); + + base::DictionaryValue new_value; + new_value.SetString(kName, kValue); + observer_.Expect(kName, &new_value); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); + + base::DictionaryValue empty; + observer_.Expect(kName, &empty); + prefs_.Set(kName, empty); + Mock::VerifyAndClearExpectations(&observer_); +} + +TEST_F(PrefServiceSetValueTest, SetListValue) { + prefs_.registry()->RegisterListPref(kName); + PrefChangeRegistrar registrar; + registrar.Init(&prefs_); + registrar.Add(kName, observer_.GetCallback()); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.RemoveUserPref(kName); + Mock::VerifyAndClearExpectations(&observer_); + + base::ListValue new_value; + new_value.AppendString(kValue); + observer_.Expect(kName, &new_value); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); + + base::ListValue empty; + observer_.Expect(kName, &empty); + prefs_.Set(kName, empty); + Mock::VerifyAndClearExpectations(&observer_); +} + +class PrefValueStoreChangeTest : public testing::Test { + protected: + PrefValueStoreChangeTest() + : user_pref_store_(base::MakeRefCounted<TestingPrefStore>()), + pref_registry_(base::MakeRefCounted<PrefRegistrySimple>()) {} + + ~PrefValueStoreChangeTest() override = default; + + void SetUp() override { + auto pref_notifier = std::make_unique<PrefNotifierImpl>(); + auto pref_value_store = std::make_unique<PrefValueStore>( + nullptr /* managed_prefs */, nullptr /* supervised_user_prefs */, + nullptr /* extension_prefs */, new TestingPrefStore(), + user_pref_store_.get(), nullptr /* recommended_prefs */, + pref_registry_->defaults().get(), pref_notifier.get()); + pref_service_ = std::make_unique<PrefService>( + std::move(pref_notifier), std::move(pref_value_store), user_pref_store_, + pref_registry_, base::DoNothing(), false); + pref_registry_->RegisterIntegerPref(kManagedPref, 1); + pref_registry_->RegisterIntegerPref(kRecommendedPref, 2); + pref_registry_->RegisterIntegerPref(kSupervisedPref, 3); + } + + std::unique_ptr<PrefService> pref_service_; + scoped_refptr<TestingPrefStore> user_pref_store_; + scoped_refptr<PrefRegistrySimple> pref_registry_; +}; + +// Check that value from the new PrefValueStore will be correctly retrieved. +TEST_F(PrefValueStoreChangeTest, ChangePrefValueStore) { + const PrefService::Preference* preference = + pref_service_->FindPreference(kManagedPref); + EXPECT_TRUE(preference->IsDefaultValue()); + EXPECT_EQ(base::Value(1), *(preference->GetValue())); + const PrefService::Preference* supervised = + pref_service_->FindPreference(kSupervisedPref); + EXPECT_TRUE(supervised->IsDefaultValue()); + EXPECT_EQ(base::Value(3), *(supervised->GetValue())); + const PrefService::Preference* recommended = + pref_service_->FindPreference(kRecommendedPref); + EXPECT_TRUE(recommended->IsDefaultValue()); + EXPECT_EQ(base::Value(2), *(recommended->GetValue())); + + user_pref_store_->SetInteger(kManagedPref, 10); + EXPECT_TRUE(preference->IsUserControlled()); + ASSERT_EQ(base::Value(10), *(preference->GetValue())); + + scoped_refptr<TestingPrefStore> managed_pref_store = + base::MakeRefCounted<TestingPrefStore>(); + pref_service_->ChangePrefValueStore( + managed_pref_store.get(), nullptr /* supervised_user_prefs */, + nullptr /* extension_prefs */, nullptr /* recommended_prefs */); + EXPECT_TRUE(preference->IsUserControlled()); + ASSERT_EQ(base::Value(10), *(preference->GetValue())); + + // Test setting a managed pref after overriding the managed PrefStore. + managed_pref_store->SetInteger(kManagedPref, 20); + EXPECT_TRUE(preference->IsManaged()); + ASSERT_EQ(base::Value(20), *(preference->GetValue())); + + // Test overriding the supervised and recommended PrefStore with already set + // prefs. + scoped_refptr<TestingPrefStore> supervised_pref_store = + base::MakeRefCounted<TestingPrefStore>(); + scoped_refptr<TestingPrefStore> recommened_pref_store = + base::MakeRefCounted<TestingPrefStore>(); + supervised_pref_store->SetInteger(kManagedPref, 30); + supervised_pref_store->SetInteger(kSupervisedPref, 31); + recommened_pref_store->SetInteger(kManagedPref, 40); + recommened_pref_store->SetInteger(kRecommendedPref, 41); + pref_service_->ChangePrefValueStore( + nullptr /* managed_prefs */, supervised_pref_store.get(), + nullptr /* extension_prefs */, recommened_pref_store.get()); + EXPECT_TRUE(preference->IsManaged()); + ASSERT_EQ(base::Value(20), *(preference->GetValue())); + EXPECT_TRUE(supervised->IsManagedByCustodian()); + EXPECT_EQ(base::Value(31), *(supervised->GetValue())); + EXPECT_TRUE(recommended->IsRecommended()); + EXPECT_EQ(base::Value(41), *(recommended->GetValue())); +} + +// Tests that PrefChangeRegistrar works after PrefValueStore is changed. +TEST_F(PrefValueStoreChangeTest, PrefChangeRegistrar) { + MockPrefChangeCallback obs(pref_service_.get()); + PrefChangeRegistrar registrar; + registrar.Init(pref_service_.get()); + registrar.Add(kManagedPref, obs.GetCallback()); + registrar.Add(kSupervisedPref, obs.GetCallback()); + registrar.Add(kRecommendedPref, obs.GetCallback()); + + base::Value expected_value(10); + obs.Expect(kManagedPref, &expected_value); + user_pref_store_->SetInteger(kManagedPref, 10); + Mock::VerifyAndClearExpectations(&obs); + expected_value = base::Value(11); + obs.Expect(kRecommendedPref, &expected_value); + user_pref_store_->SetInteger(kRecommendedPref, 11); + Mock::VerifyAndClearExpectations(&obs); + + // Test overriding the managed and supervised PrefStore with already set + // prefs. + scoped_refptr<TestingPrefStore> managed_pref_store = + base::MakeRefCounted<TestingPrefStore>(); + scoped_refptr<TestingPrefStore> supervised_pref_store = + base::MakeRefCounted<TestingPrefStore>(); + // Update |kManagedPref| before changing the PrefValueStore, the + // PrefChangeRegistrar should get notified on |kManagedPref| as its value + // changes. + managed_pref_store->SetInteger(kManagedPref, 20); + // Due to store precedence, the value of |kRecommendedPref| will not be + // changed so PrefChangeRegistrar will not be notified. + managed_pref_store->SetInteger(kRecommendedPref, 11); + supervised_pref_store->SetInteger(kManagedPref, 30); + supervised_pref_store->SetInteger(kRecommendedPref, 21); + expected_value = base::Value(20); + obs.Expect(kManagedPref, &expected_value); + pref_service_->ChangePrefValueStore( + managed_pref_store.get(), supervised_pref_store.get(), + nullptr /* extension_prefs */, nullptr /* recommended_prefs */); + Mock::VerifyAndClearExpectations(&obs); + + // Update a pref value after PrefValueStore change, it should also work. + expected_value = base::Value(31); + obs.Expect(kSupervisedPref, &expected_value); + supervised_pref_store->SetInteger(kSupervisedPref, 31); + Mock::VerifyAndClearExpectations(&obs); +}
diff --git a/src/updater_components/prefs/pref_store.cc b/src/updater_components/prefs/pref_store.cc new file mode 100644 index 0000000..2ae0afc --- /dev/null +++ b/src/updater_components/prefs/pref_store.cc
@@ -0,0 +1,13 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_store.h" + +bool PrefStore::HasObservers() const { + return false; +} + +bool PrefStore::IsInitializationComplete() const { + return true; +}
diff --git a/src/updater_components/prefs/pref_store.h b/src/updater_components/prefs/pref_store.h new file mode 100644 index 0000000..5f5f092 --- /dev/null +++ b/src/updater_components/prefs/pref_store.h
@@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_STORE_H_ +#define COMPONENTS_PREFS_PREF_STORE_H_ + +#include <memory> +#include <string> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/prefs/prefs_export.h" + +namespace base { +class DictionaryValue; +class Value; +} + +// This is an abstract interface for reading and writing from/to a persistent +// preference store, used by PrefService. An implementation using a JSON file +// can be found in JsonPrefStore, while an implementation without any backing +// store for testing can be found in TestingPrefStore. Furthermore, there is +// CommandLinePrefStore, which bridges command line options to preferences and +// ConfigurationPolicyPrefStore, which is used for hooking up configuration +// policy with the preference subsystem. +class COMPONENTS_PREFS_EXPORT PrefStore : public base::RefCounted<PrefStore> { + public: + // Observer interface for monitoring PrefStore. + class COMPONENTS_PREFS_EXPORT Observer { + public: + // Called when the value for the given |key| in the store changes. + virtual void OnPrefValueChanged(const std::string& key) = 0; + // Notification about the PrefStore being fully initialized. + virtual void OnInitializationCompleted(bool succeeded) = 0; + + protected: + virtual ~Observer() {} + }; + + PrefStore() {} + + // Add and remove observers. + virtual void AddObserver(Observer* observer) {} + virtual void RemoveObserver(Observer* observer) {} + virtual bool HasObservers() const; + + // Whether the store has completed all asynchronous initialization. + virtual bool IsInitializationComplete() const; + + // Get the value for a given preference |key| and stores it in |*result|. + // |*result| is only modified if the return value is true and if |result| + // is not NULL. Ownership of the |*result| value remains with the PrefStore. + virtual bool GetValue(const std::string& key, + const base::Value** result) const = 0; + + // Get all the values. Never returns a null pointer. + virtual std::unique_ptr<base::DictionaryValue> GetValues() const = 0; + + protected: + friend class base::RefCounted<PrefStore>; + virtual ~PrefStore() {} + + private: + DISALLOW_COPY_AND_ASSIGN(PrefStore); +}; + +#endif // COMPONENTS_PREFS_PREF_STORE_H_
diff --git a/src/updater_components/prefs/pref_store_observer_mock.cc b/src/updater_components/prefs/pref_store_observer_mock.cc new file mode 100644 index 0000000..a03c858 --- /dev/null +++ b/src/updater_components/prefs/pref_store_observer_mock.cc
@@ -0,0 +1,29 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_store_observer_mock.h" + +#include "testing/gtest/include/gtest/gtest.h" + +PrefStoreObserverMock::PrefStoreObserverMock() + : initialized(false), initialization_success(false) {} + +PrefStoreObserverMock::~PrefStoreObserverMock() {} + +void PrefStoreObserverMock::VerifyAndResetChangedKey( + const std::string& expected) { + EXPECT_EQ(1u, changed_keys.size()); + if (changed_keys.size() >= 1) + EXPECT_EQ(expected, changed_keys.front()); + changed_keys.clear(); +} + +void PrefStoreObserverMock::OnPrefValueChanged(const std::string& key) { + changed_keys.push_back(key); +} + +void PrefStoreObserverMock::OnInitializationCompleted(bool success) { + initialized = true; + initialization_success = success; +}
diff --git a/src/updater_components/prefs/pref_store_observer_mock.h b/src/updater_components/prefs/pref_store_observer_mock.h new file mode 100644 index 0000000..01bd6a6 --- /dev/null +++ b/src/updater_components/prefs/pref_store_observer_mock.h
@@ -0,0 +1,35 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_ +#define COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_ + +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "components/prefs/pref_store.h" + +// A mock implementation of PrefStore::Observer. +class PrefStoreObserverMock : public PrefStore::Observer { + public: + PrefStoreObserverMock(); + ~PrefStoreObserverMock() override; + + void VerifyAndResetChangedKey(const std::string& expected); + + // PrefStore::Observer implementation + void OnPrefValueChanged(const std::string& key) override; + void OnInitializationCompleted(bool success) override; + + std::vector<std::string> changed_keys; + bool initialized; + bool initialization_success; // Only valid if |initialized|. + + private: + DISALLOW_COPY_AND_ASSIGN(PrefStoreObserverMock); +}; + +#endif // COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_
diff --git a/src/updater_components/prefs/pref_value_map.cc b/src/updater_components/prefs/pref_value_map.cc new file mode 100644 index 0000000..b932eef --- /dev/null +++ b/src/updater_components/prefs/pref_value_map.cc
@@ -0,0 +1,161 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_value_map.h" + +#include <map> +#include <memory> +#include <utility> + +#include "base/logging.h" +#include "base/values.h" + +PrefValueMap::PrefValueMap() {} + +PrefValueMap::~PrefValueMap() {} + +bool PrefValueMap::GetValue(const std::string& key, + const base::Value** value) const { + auto it = prefs_.find(key); + if (it == prefs_.end()) + return false; + + if (value) + *value = &it->second; + + return true; +} + +bool PrefValueMap::GetValue(const std::string& key, base::Value** value) { + auto it = prefs_.find(key); + if (it == prefs_.end()) + return false; + + if (value) + *value = &it->second; + + return true; +} + +bool PrefValueMap::SetValue(const std::string& key, base::Value value) { + base::Value& existing_value = prefs_[key]; + if (value == existing_value) + return false; + + existing_value = std::move(value); + return true; +} + +bool PrefValueMap::RemoveValue(const std::string& key) { + return prefs_.erase(key) != 0; +} + +void PrefValueMap::Clear() { + prefs_.clear(); +} + +void PrefValueMap::Swap(PrefValueMap* other) { + prefs_.swap(other->prefs_); +} + +PrefValueMap::iterator PrefValueMap::begin() { + return prefs_.begin(); +} + +PrefValueMap::iterator PrefValueMap::end() { + return prefs_.end(); +} + +PrefValueMap::const_iterator PrefValueMap::begin() const { + return prefs_.begin(); +} + +PrefValueMap::const_iterator PrefValueMap::end() const { + return prefs_.end(); +} + +bool PrefValueMap::empty() const { + return prefs_.empty(); +} + +bool PrefValueMap::GetBoolean(const std::string& key, + bool* value) const { + const base::Value* stored_value = nullptr; + return GetValue(key, &stored_value) && stored_value->GetAsBoolean(value); +} + +void PrefValueMap::SetBoolean(const std::string& key, bool value) { + SetValue(key, base::Value(value)); +} + +bool PrefValueMap::GetString(const std::string& key, + std::string* value) const { + const base::Value* stored_value = nullptr; + return GetValue(key, &stored_value) && stored_value->GetAsString(value); +} + +void PrefValueMap::SetString(const std::string& key, + const std::string& value) { + SetValue(key, base::Value(value)); +} + +bool PrefValueMap::GetInteger(const std::string& key, int* value) const { + const base::Value* stored_value = nullptr; + return GetValue(key, &stored_value) && stored_value->GetAsInteger(value); +} + +void PrefValueMap::SetInteger(const std::string& key, const int value) { + SetValue(key, base::Value(value)); +} + +void PrefValueMap::SetDouble(const std::string& key, const double value) { + SetValue(key, base::Value(value)); +} + +void PrefValueMap::GetDifferingKeys( + const PrefValueMap* other, + std::vector<std::string>* differing_keys) const { + differing_keys->clear(); + + // Put everything into ordered maps. + std::map<std::string, const base::Value*> this_prefs; + std::map<std::string, const base::Value*> other_prefs; + for (const auto& pair : prefs_) + this_prefs.emplace(pair.first, &pair.second); + for (const auto& pair : other->prefs_) + other_prefs.emplace(pair.first, &pair.second); + + // Walk over the maps in lockstep, adding everything that is different. + auto this_pref = this_prefs.begin(); + auto other_pref = other_prefs.begin(); + while (this_pref != this_prefs.end() && other_pref != other_prefs.end()) { + const int diff = this_pref->first.compare(other_pref->first); + if (diff == 0) { + if (!this_pref->second->Equals(other_pref->second)) + differing_keys->push_back(this_pref->first); + ++this_pref; + ++other_pref; + } else if (diff < 0) { + differing_keys->push_back(this_pref->first); + ++this_pref; + } else if (diff > 0) { + differing_keys->push_back(other_pref->first); + ++other_pref; + } + } + + // Add the remaining entries. + for ( ; this_pref != this_prefs.end(); ++this_pref) + differing_keys->push_back(this_pref->first); + for ( ; other_pref != other_prefs.end(); ++other_pref) + differing_keys->push_back(other_pref->first); +} + +std::unique_ptr<base::DictionaryValue> PrefValueMap::AsDictionaryValue() const { + auto dictionary = std::make_unique<base::DictionaryValue>(); + for (const auto& value : prefs_) + dictionary->Set(value.first, value.second.CreateDeepCopy()); + + return dictionary; +}
diff --git a/src/updater_components/prefs/pref_value_map.h b/src/updater_components/prefs/pref_value_map.h new file mode 100644 index 0000000..6a9b4b6 --- /dev/null +++ b/src/updater_components/prefs/pref_value_map.h
@@ -0,0 +1,95 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_VALUE_MAP_H_ +#define COMPONENTS_PREFS_PREF_VALUE_MAP_H_ + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "components/prefs/prefs_export.h" + +namespace base { +class DictionaryValue; +class Value; +} + +// A generic string to value map used by the PrefStore implementations. +class COMPONENTS_PREFS_EXPORT PrefValueMap { + public: + using Map = std::map<std::string, base::Value>; + using iterator = Map::iterator; + using const_iterator = Map::const_iterator; + + PrefValueMap(); + virtual ~PrefValueMap(); + + // Gets the value for |key| and stores it in |value|. Ownership remains with + // the map. Returns true if a value is present. If not, |value| is not + // touched. + bool GetValue(const std::string& key, const base::Value** value) const; + bool GetValue(const std::string& key, base::Value** value); + + // Sets a new |value| for |key|. Returns true if the value changed. + bool SetValue(const std::string& key, base::Value value); + + // Removes the value for |key| from the map. Returns true if a value was + // removed. + bool RemoveValue(const std::string& key); + + // Clears the map. + void Clear(); + + // Swaps the contents of two maps. + void Swap(PrefValueMap* other); + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + bool empty() const; + + // Gets a boolean value for |key| and stores it in |value|. Returns true if + // the value was found and of the proper type. + bool GetBoolean(const std::string& key, bool* value) const; + + // Sets the value for |key| to the boolean |value|. + void SetBoolean(const std::string& key, bool value); + + // Gets a string value for |key| and stores it in |value|. Returns true if + // the value was found and of the proper type. + bool GetString(const std::string& key, std::string* value) const; + + // Sets the value for |key| to the string |value|. + void SetString(const std::string& key, const std::string& value); + + // Gets an int value for |key| and stores it in |value|. Returns true if + // the value was found and of the proper type. + bool GetInteger(const std::string& key, int* value) const; + + // Sets the value for |key| to the int |value|. + void SetInteger(const std::string& key, const int value); + + // Sets the value for |key| to the double |value|. + void SetDouble(const std::string& key, const double value); + + // Compares this value map against |other| and stores all key names that have + // different values in |differing_keys|. This includes keys that are present + // only in one of the maps. + void GetDifferingKeys(const PrefValueMap* other, + std::vector<std::string>* differing_keys) const; + + // Copies the map into a dictionary value. + std::unique_ptr<base::DictionaryValue> AsDictionaryValue() const; + + private: + Map prefs_; + + DISALLOW_COPY_AND_ASSIGN(PrefValueMap); +}; + +#endif // COMPONENTS_PREFS_PREF_VALUE_MAP_H_
diff --git a/src/updater_components/prefs/pref_value_map_unittest.cc b/src/updater_components/prefs/pref_value_map_unittest.cc new file mode 100644 index 0000000..7d2f05c --- /dev/null +++ b/src/updater_components/prefs/pref_value_map_unittest.cc
@@ -0,0 +1,126 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_value_map.h" + +#include "base/values.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +TEST(PrefValueMapTest, SetValue) { + PrefValueMap map; + const Value* result = nullptr; + EXPECT_FALSE(map.GetValue("key", &result)); + EXPECT_FALSE(result); + + EXPECT_TRUE(map.SetValue("key", Value("test"))); + EXPECT_FALSE(map.SetValue("key", Value("test"))); + EXPECT_TRUE(map.SetValue("key", Value("hi mom!"))); + + EXPECT_TRUE(map.GetValue("key", &result)); + EXPECT_TRUE(Value("hi mom!").Equals(result)); +} + +TEST(PrefValueMapTest, GetAndSetIntegerValue) { + PrefValueMap map; + ASSERT_TRUE(map.SetValue("key", Value(5))); + + int int_value = 0; + EXPECT_TRUE(map.GetInteger("key", &int_value)); + EXPECT_EQ(5, int_value); + + map.SetInteger("key", -14); + EXPECT_TRUE(map.GetInteger("key", &int_value)); + EXPECT_EQ(-14, int_value); +} + +TEST(PrefValueMapTest, SetDoubleValue) { + PrefValueMap map; + ASSERT_TRUE(map.SetValue("key", Value(5.5))); + + const Value* result = nullptr; + ASSERT_TRUE(map.GetValue("key", &result)); + double double_value = 0.; + EXPECT_TRUE(result->GetAsDouble(&double_value)); + EXPECT_DOUBLE_EQ(5.5, double_value); +} + +TEST(PrefValueMapTest, RemoveValue) { + PrefValueMap map; + EXPECT_FALSE(map.RemoveValue("key")); + + EXPECT_TRUE(map.SetValue("key", Value("test"))); + EXPECT_TRUE(map.GetValue("key", nullptr)); + + EXPECT_TRUE(map.RemoveValue("key")); + EXPECT_FALSE(map.GetValue("key", nullptr)); + + EXPECT_FALSE(map.RemoveValue("key")); +} + +TEST(PrefValueMapTest, Clear) { + PrefValueMap map; + EXPECT_TRUE(map.SetValue("key", Value("test"))); + EXPECT_TRUE(map.GetValue("key", nullptr)); + + map.Clear(); + + EXPECT_FALSE(map.GetValue("key", nullptr)); +} + +TEST(PrefValueMapTest, GetDifferingKeys) { + PrefValueMap reference; + EXPECT_TRUE(reference.SetValue("b", Value("test"))); + EXPECT_TRUE(reference.SetValue("c", Value("test"))); + EXPECT_TRUE(reference.SetValue("e", Value("test"))); + + PrefValueMap check; + std::vector<std::string> differing_paths; + std::vector<std::string> expected_differing_paths; + + reference.GetDifferingKeys(&check, &differing_paths); + expected_differing_paths.push_back("b"); + expected_differing_paths.push_back("c"); + expected_differing_paths.push_back("e"); + EXPECT_EQ(expected_differing_paths, differing_paths); + + EXPECT_TRUE(check.SetValue("a", Value("test"))); + EXPECT_TRUE(check.SetValue("c", Value("test"))); + EXPECT_TRUE(check.SetValue("d", Value("test"))); + + reference.GetDifferingKeys(&check, &differing_paths); + expected_differing_paths.clear(); + expected_differing_paths.push_back("a"); + expected_differing_paths.push_back("b"); + expected_differing_paths.push_back("d"); + expected_differing_paths.push_back("e"); + EXPECT_EQ(expected_differing_paths, differing_paths); +} + +TEST(PrefValueMapTest, SwapTwoMaps) { + PrefValueMap first_map; + EXPECT_TRUE(first_map.SetValue("a", Value("test"))); + EXPECT_TRUE(first_map.SetValue("b", Value("test"))); + EXPECT_TRUE(first_map.SetValue("c", Value("test"))); + + PrefValueMap second_map; + EXPECT_TRUE(second_map.SetValue("d", Value("test"))); + EXPECT_TRUE(second_map.SetValue("e", Value("test"))); + EXPECT_TRUE(second_map.SetValue("f", Value("test"))); + + first_map.Swap(&second_map); + + EXPECT_TRUE(first_map.GetValue("d", nullptr)); + EXPECT_TRUE(first_map.GetValue("e", nullptr)); + EXPECT_TRUE(first_map.GetValue("f", nullptr)); + + EXPECT_TRUE(second_map.GetValue("a", nullptr)); + EXPECT_TRUE(second_map.GetValue("b", nullptr)); + EXPECT_TRUE(second_map.GetValue("c", nullptr)); +} + +} // namespace +} // namespace base
diff --git a/src/updater_components/prefs/pref_value_store.cc b/src/updater_components/prefs/pref_value_store.cc new file mode 100644 index 0000000..a7aecdd --- /dev/null +++ b/src/updater_components/prefs/pref_value_store.cc
@@ -0,0 +1,312 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_value_store.h" + +#include <stddef.h> + +#include "base/logging.h" +#include "components/prefs/pref_notifier.h" +#include "components/prefs/pref_observer.h" + +PrefValueStore::PrefStoreKeeper::PrefStoreKeeper() + : pref_value_store_(nullptr), type_(PrefValueStore::INVALID_STORE) {} + +PrefValueStore::PrefStoreKeeper::~PrefStoreKeeper() { + if (pref_store_) { + pref_store_->RemoveObserver(this); + pref_store_ = nullptr; + } + pref_value_store_ = nullptr; +} + +void PrefValueStore::PrefStoreKeeper::Initialize( + PrefValueStore* store, + PrefStore* pref_store, + PrefValueStore::PrefStoreType type) { + if (pref_store_) { + pref_store_->RemoveObserver(this); + DCHECK(!pref_store_->HasObservers()); + } + type_ = type; + pref_value_store_ = store; + pref_store_ = pref_store; + if (pref_store_) + pref_store_->AddObserver(this); +} + +void PrefValueStore::PrefStoreKeeper::OnPrefValueChanged( + const std::string& key) { + pref_value_store_->OnPrefValueChanged(type_, key); +} + +void PrefValueStore::PrefStoreKeeper::OnInitializationCompleted( + bool succeeded) { + pref_value_store_->OnInitializationCompleted(type_, succeeded); +} + +PrefValueStore::PrefValueStore(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier, + std::unique_ptr<Delegate> delegate) + : pref_notifier_(pref_notifier), + initialization_failed_(false), + delegate_(std::move(delegate)) { + InitPrefStore(MANAGED_STORE, managed_prefs); + InitPrefStore(SUPERVISED_USER_STORE, supervised_user_prefs); + InitPrefStore(EXTENSION_STORE, extension_prefs); + InitPrefStore(COMMAND_LINE_STORE, command_line_prefs); + InitPrefStore(USER_STORE, user_prefs); + InitPrefStore(RECOMMENDED_STORE, recommended_prefs); + InitPrefStore(DEFAULT_STORE, default_prefs); + + CheckInitializationCompleted(); + if (delegate_) { + delegate_->Init(managed_prefs, supervised_user_prefs, extension_prefs, + command_line_prefs, user_prefs, recommended_prefs, + default_prefs, pref_notifier); + } +} + +PrefValueStore::~PrefValueStore() {} + +std::unique_ptr<PrefValueStore> PrefValueStore::CloneAndSpecialize( + PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier, + std::unique_ptr<Delegate> delegate) { + DCHECK(pref_notifier); + if (!managed_prefs) + managed_prefs = GetPrefStore(MANAGED_STORE); + if (!supervised_user_prefs) + supervised_user_prefs = GetPrefStore(SUPERVISED_USER_STORE); + if (!extension_prefs) + extension_prefs = GetPrefStore(EXTENSION_STORE); + if (!command_line_prefs) + command_line_prefs = GetPrefStore(COMMAND_LINE_STORE); + if (!user_prefs) + user_prefs = GetPrefStore(USER_STORE); + if (!recommended_prefs) + recommended_prefs = GetPrefStore(RECOMMENDED_STORE); + if (!default_prefs) + default_prefs = GetPrefStore(DEFAULT_STORE); + + return std::make_unique<PrefValueStore>( + managed_prefs, supervised_user_prefs, extension_prefs, command_line_prefs, + user_prefs, recommended_prefs, default_prefs, pref_notifier, + std::move(delegate)); +} + +void PrefValueStore::set_callback(const PrefChangedCallback& callback) { + pref_changed_callback_ = callback; +} + +bool PrefValueStore::GetValue(const std::string& name, + base::Value::Type type, + const base::Value** out_value) const { + // Check the |PrefStore|s in order of their priority from highest to lowest, + // looking for the first preference value with the given |name| and |type|. + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + if (GetValueFromStoreWithType(name, type, static_cast<PrefStoreType>(i), + out_value)) + return true; + } + return false; +} + +bool PrefValueStore::GetRecommendedValue(const std::string& name, + base::Value::Type type, + const base::Value** out_value) const { + return GetValueFromStoreWithType(name, type, RECOMMENDED_STORE, out_value); +} + +void PrefValueStore::NotifyPrefChanged( + const std::string& path, + PrefValueStore::PrefStoreType new_store) { + DCHECK(new_store != INVALID_STORE); + // A notification is sent when the pref value in any store changes. If this + // store is currently being overridden by a higher-priority store, the + // effective value of the pref will not have changed. + pref_notifier_->OnPreferenceChanged(path); + if (!pref_changed_callback_.is_null()) + pref_changed_callback_.Run(path); +} + +bool PrefValueStore::PrefValueInManagedStore(const std::string& name) const { + return PrefValueInStore(name, MANAGED_STORE); +} + +bool PrefValueStore::PrefValueInSupervisedStore(const std::string& name) const { + return PrefValueInStore(name, SUPERVISED_USER_STORE); +} + +bool PrefValueStore::PrefValueInExtensionStore(const std::string& name) const { + return PrefValueInStore(name, EXTENSION_STORE); +} + +bool PrefValueStore::PrefValueInUserStore(const std::string& name) const { + return PrefValueInStore(name, USER_STORE); +} + +bool PrefValueStore::PrefValueFromExtensionStore( + const std::string& name) const { + return ControllingPrefStoreForPref(name) == EXTENSION_STORE; +} + +bool PrefValueStore::PrefValueFromUserStore(const std::string& name) const { + return ControllingPrefStoreForPref(name) == USER_STORE; +} + +bool PrefValueStore::PrefValueFromRecommendedStore( + const std::string& name) const { + return ControllingPrefStoreForPref(name) == RECOMMENDED_STORE; +} + +bool PrefValueStore::PrefValueFromDefaultStore(const std::string& name) const { + return ControllingPrefStoreForPref(name) == DEFAULT_STORE; +} + +bool PrefValueStore::PrefValueUserModifiable(const std::string& name) const { + PrefStoreType effective_store = ControllingPrefStoreForPref(name); + return effective_store >= USER_STORE || + effective_store == INVALID_STORE; +} + +bool PrefValueStore::PrefValueExtensionModifiable( + const std::string& name) const { + PrefStoreType effective_store = ControllingPrefStoreForPref(name); + return effective_store >= EXTENSION_STORE || + effective_store == INVALID_STORE; +} + +void PrefValueStore::UpdateCommandLinePrefStore(PrefStore* command_line_prefs) { + InitPrefStore(COMMAND_LINE_STORE, command_line_prefs); + if (delegate_) + delegate_->UpdateCommandLinePrefStore(command_line_prefs); +} + +bool PrefValueStore::IsInitializationComplete() const { + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + const PrefStore* pref_store = GetPrefStore(static_cast<PrefStoreType>(i)); + if (pref_store && !pref_store->IsInitializationComplete()) + return false; + } + return true; +} + +bool PrefValueStore::HasPrefStore(PrefStoreType type) const { + return GetPrefStore(type); +} + +bool PrefValueStore::PrefValueInStore( + const std::string& name, + PrefValueStore::PrefStoreType store) const { + // Declare a temp Value* and call GetValueFromStore, + // ignoring the output value. + const base::Value* tmp_value = nullptr; + return GetValueFromStore(name, store, &tmp_value); +} + +bool PrefValueStore::PrefValueInStoreRange( + const std::string& name, + PrefValueStore::PrefStoreType first_checked_store, + PrefValueStore::PrefStoreType last_checked_store) const { + if (first_checked_store > last_checked_store) { + NOTREACHED(); + return false; + } + + for (size_t i = first_checked_store; + i <= static_cast<size_t>(last_checked_store); ++i) { + if (PrefValueInStore(name, static_cast<PrefStoreType>(i))) + return true; + } + return false; +} + +PrefValueStore::PrefStoreType PrefValueStore::ControllingPrefStoreForPref( + const std::string& name) const { + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + if (PrefValueInStore(name, static_cast<PrefStoreType>(i))) + return static_cast<PrefStoreType>(i); + } + return INVALID_STORE; +} + +bool PrefValueStore::GetValueFromStore(const std::string& name, + PrefValueStore::PrefStoreType store_type, + const base::Value** out_value) const { + // Only return true if we find a value and it is the correct type, so stale + // values with the incorrect type will be ignored. + const PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(store_type)); + if (store && store->GetValue(name, out_value)) + return true; + + // No valid value found for the given preference name: set the return value + // to false. + *out_value = nullptr; + return false; +} + +bool PrefValueStore::GetValueFromStoreWithType( + const std::string& name, + base::Value::Type type, + PrefStoreType store, + const base::Value** out_value) const { + if (GetValueFromStore(name, store, out_value)) { + if ((*out_value)->type() == type) + return true; + + LOG(WARNING) << "Expected type for " << name << " is " << type + << " but got " << (*out_value)->type() << " in store " + << store; + } + + *out_value = nullptr; + return false; +} + +void PrefValueStore::OnPrefValueChanged(PrefValueStore::PrefStoreType type, + const std::string& key) { + NotifyPrefChanged(key, type); +} + +void PrefValueStore::OnInitializationCompleted( + PrefValueStore::PrefStoreType type, bool succeeded) { + if (initialization_failed_) + return; + if (!succeeded) { + initialization_failed_ = true; + pref_notifier_->OnInitializationCompleted(false); + return; + } + CheckInitializationCompleted(); +} + +void PrefValueStore::InitPrefStore(PrefValueStore::PrefStoreType type, + PrefStore* pref_store) { + pref_stores_[type].Initialize(this, pref_store, type); +} + +void PrefValueStore::CheckInitializationCompleted() { + if (initialization_failed_) + return; + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + scoped_refptr<PrefStore> store = + GetPrefStore(static_cast<PrefStoreType>(i)); + if (store.get() && !store->IsInitializationComplete()) + return; + } + pref_notifier_->OnInitializationCompleted(true); +}
diff --git a/src/updater_components/prefs/pref_value_store.h b/src/updater_components/prefs/pref_value_store.h new file mode 100644 index 0000000..70a92bc --- /dev/null +++ b/src/updater_components/prefs/pref_value_store.h
@@ -0,0 +1,318 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_PREF_VALUE_STORE_H_ +#define COMPONENTS_PREFS_PREF_VALUE_STORE_H_ + +#include <functional> +#include <map> +#include <memory> +#include <string> +#include <type_traits> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/values.h" +#include "components/prefs/pref_store.h" +#include "components/prefs/prefs_export.h" + +class PersistentPrefStore; +class PrefNotifier; +class PrefRegistry; +class PrefStore; + +// The PrefValueStore manages various sources of values for Preferences +// (e.g., configuration policies, extensions, and user settings). It returns +// the value of a Preference from the source with the highest priority, and +// allows setting user-defined values for preferences that are not managed. +// +// Unless otherwise explicitly noted, all of the methods of this class must +// be called on the UI thread. +class COMPONENTS_PREFS_EXPORT PrefValueStore { + public: + typedef base::Callback<void(const std::string&)> PrefChangedCallback; + + // Delegate used to observe certain events in the |PrefValueStore|'s lifetime. + class Delegate { + public: + virtual ~Delegate() {} + + // Called by the PrefValueStore constructor with the PrefStores passed to + // it. + virtual void Init(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier) = 0; + + virtual void InitIncognitoUserPrefs( + scoped_refptr<PersistentPrefStore> incognito_user_prefs_overlay, + scoped_refptr<PersistentPrefStore> incognito_user_prefs_underlay, + const std::vector<const char*>& overlay_pref_names) = 0; + + virtual void InitPrefRegistry(PrefRegistry* pref_registry) = 0; + + // Called whenever PrefValueStore::UpdateCommandLinePrefStore is called, + // with the same argument. + virtual void UpdateCommandLinePrefStore(PrefStore* command_line_prefs) = 0; + }; + + // PrefStores must be listed here in order from highest to lowest priority. + // MANAGED contains all managed preference values that are provided by + // mandatory policies (e.g. Windows Group Policy or cloud policy). + // SUPERVISED_USER contains preferences that are valid for supervised users. + // EXTENSION contains preference values set by extensions. + // COMMAND_LINE contains preference values set by command-line switches. + // USER contains all user-set preference values. + // RECOMMENDED contains all preferences that are provided by recommended + // policies. + // DEFAULT contains all application default preference values. + enum PrefStoreType { + // INVALID_STORE is not associated with an actual PrefStore but used as + // an invalid marker, e.g. as a return value. + INVALID_STORE = -1, + MANAGED_STORE = 0, + SUPERVISED_USER_STORE, + EXTENSION_STORE, + COMMAND_LINE_STORE, + USER_STORE, + RECOMMENDED_STORE, + DEFAULT_STORE, + PREF_STORE_TYPE_MAX = DEFAULT_STORE + }; + + // In decreasing order of precedence: + // |managed_prefs| contains all preferences from mandatory policies. + // |supervised_user_prefs| contains all preferences from supervised user + // settings, i.e. settings configured for a supervised user by their + // custodian. + // |extension_prefs| contains preference values set by extensions. + // |command_line_prefs| contains preference values set by command-line + // switches. + // |user_prefs| contains all user-set preference values. + // |recommended_prefs| contains all preferences from recommended policies. + // |default_prefs| contains application-default preference values. It must + // be non-null if any preferences are to be registered. + // + // |pref_notifier| facilitates broadcasting preference change notifications + // to the world. + PrefValueStore(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier, + std::unique_ptr<Delegate> delegate = nullptr); + virtual ~PrefValueStore(); + + // Creates a clone of this PrefValueStore with PrefStores overwritten + // by the parameters passed, if unequal NULL. + // + // The new PrefValueStore is passed the |delegate| in its constructor. + std::unique_ptr<PrefValueStore> CloneAndSpecialize( + PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier, + std::unique_ptr<Delegate> delegate = nullptr); + + // A PrefValueStore can have exactly one callback that is directly + // notified of preferences changing in the store. This does not + // filter through the PrefNotifier mechanism, which may not forward + // certain changes (e.g. unregistered prefs). + void set_callback(const PrefChangedCallback& callback); + + // Gets the value for the given preference name that has the specified value + // type. Values stored in a PrefStore that have the matching |name| but + // a non-matching |type| are silently skipped. Returns true if a valid value + // was found in any of the available PrefStores. Most callers should use + // Preference::GetValue() instead of calling this method directly. + bool GetValue(const std::string& name, + base::Value::Type type, + const base::Value** out_value) const; + + // Gets the recommended value for the given preference name that has the + // specified value type. A value stored in the recommended PrefStore that has + // the matching |name| but a non-matching |type| is silently ignored. Returns + // true if a valid value was found. Most callers should use + // Preference::GetRecommendedValue() instead of calling this method directly. + bool GetRecommendedValue(const std::string& name, + base::Value::Type type, + const base::Value** out_value) const; + + // These methods return true if a preference with the given name is in the + // indicated pref store, even if that value is currently being overridden by + // a higher-priority source. + bool PrefValueInManagedStore(const std::string& name) const; + bool PrefValueInSupervisedStore(const std::string& name) const; + bool PrefValueInExtensionStore(const std::string& name) const; + bool PrefValueInUserStore(const std::string& name) const; + + // These methods return true if a preference with the given name is actually + // being controlled by the indicated pref store and not being overridden by + // a higher-priority source. + bool PrefValueFromExtensionStore(const std::string& name) const; + bool PrefValueFromUserStore(const std::string& name) const; + bool PrefValueFromRecommendedStore(const std::string& name) const; + bool PrefValueFromDefaultStore(const std::string& name) const; + + // Check whether a Preference value is modifiable by the user, i.e. whether + // there is no higher-priority source controlling it. + bool PrefValueUserModifiable(const std::string& name) const; + + // Check whether a Preference value is modifiable by an extension, i.e. + // whether there is no higher-priority source controlling it. + bool PrefValueExtensionModifiable(const std::string& name) const; + + // Update the command line PrefStore with |command_line_prefs|. + void UpdateCommandLinePrefStore(PrefStore* command_line_prefs); + + bool IsInitializationComplete() const; + + // Check whether a particular type of PrefStore exists. + bool HasPrefStore(PrefStoreType type) const; + + private: + // Keeps a PrefStore reference on behalf of the PrefValueStore and monitors + // the PrefStore for changes, forwarding notifications to PrefValueStore. This + // indirection is here for the sake of disambiguating notifications from the + // individual PrefStores. + class PrefStoreKeeper : public PrefStore::Observer { + public: + PrefStoreKeeper(); + ~PrefStoreKeeper() override; + + // Takes ownership of |pref_store|. + void Initialize(PrefValueStore* store, + PrefStore* pref_store, + PrefStoreType type); + + PrefStore* store() { return pref_store_.get(); } + const PrefStore* store() const { return pref_store_.get(); } + + private: + // PrefStore::Observer implementation. + void OnPrefValueChanged(const std::string& key) override; + void OnInitializationCompleted(bool succeeded) override; + + // PrefValueStore this keeper is part of. + PrefValueStore* pref_value_store_; + + // The PrefStore managed by this keeper. + scoped_refptr<PrefStore> pref_store_; + + // Type of the pref store. + PrefStoreType type_; + + DISALLOW_COPY_AND_ASSIGN(PrefStoreKeeper); + }; + + typedef std::map<std::string, base::Value::Type> PrefTypeMap; + + // Returns true if the preference with the given name has a value in the + // given PrefStoreType, of the same value type as the preference was + // registered with. + bool PrefValueInStore(const std::string& name, PrefStoreType store) const; + + // Returns true if a preference has an explicit value in any of the + // stores in the range specified by |first_checked_store| and + // |last_checked_store|, even if that value is currently being + // overridden by a higher-priority store. + bool PrefValueInStoreRange(const std::string& name, + PrefStoreType first_checked_store, + PrefStoreType last_checked_store) const; + + // Returns the pref store type identifying the source that controls the + // Preference identified by |name|. If none of the sources has a value, + // INVALID_STORE is returned. In practice, the default PrefStore + // should always have a value for any registered preferencem, so INVALID_STORE + // indicates an error. + PrefStoreType ControllingPrefStoreForPref(const std::string& name) const; + + // Get a value from the specified |store|. + bool GetValueFromStore(const std::string& name, + PrefStoreType store, + const base::Value** out_value) const; + + // Get a value from the specified |store| if its |type| matches. + bool GetValueFromStoreWithType(const std::string& name, + base::Value::Type type, + PrefStoreType store, + const base::Value** out_value) const; + + // Called upon changes in individual pref stores in order to determine whether + // the user-visible pref value has changed. Triggers the change notification + // if the effective value of the preference has changed, or if the store + // controlling the pref has changed. + void NotifyPrefChanged(const std::string& path, PrefStoreType new_store); + + // Called from the PrefStoreKeeper implementation when a pref value for |key| + // changed in the pref store for |type|. + void OnPrefValueChanged(PrefStoreType type, const std::string& key); + + // Handle the event that the store for |type| has completed initialization. + void OnInitializationCompleted(PrefStoreType type, bool succeeded); + + // Initializes a pref store keeper. Sets up a PrefStoreKeeper that will take + // ownership of the passed |pref_store|. + void InitPrefStore(PrefStoreType type, PrefStore* pref_store); + + // Checks whether initialization is completed and tells the notifier if that + // is the case. + void CheckInitializationCompleted(); + + // Get the PrefStore pointer for the given type. May return NULL if there is + // no PrefStore for that type. + PrefStore* GetPrefStore(PrefStoreType type) { + return pref_stores_[type].store(); + } + const PrefStore* GetPrefStore(PrefStoreType type) const { + return pref_stores_[type].store(); + } + + // Keeps the PrefStore references in order of precedence. + PrefStoreKeeper pref_stores_[PREF_STORE_TYPE_MAX + 1]; + + PrefChangedCallback pref_changed_callback_; + + // Used for generating notifications. This is a weak reference, + // since the notifier is owned by the corresponding PrefService. + PrefNotifier* pref_notifier_; + + // A mapping of preference names to their registered types. + PrefTypeMap pref_types_; + + // True if not all of the PrefStores were initialized successfully. + bool initialization_failed_; + + // Might be null. + std::unique_ptr<Delegate> delegate_; + + DISALLOW_COPY_AND_ASSIGN(PrefValueStore); +}; + +namespace std { + +template <> +struct hash<PrefValueStore::PrefStoreType> { + size_t operator()(PrefValueStore::PrefStoreType type) const { + return std::hash< + std::underlying_type<PrefValueStore::PrefStoreType>::type>()(type); + } +}; + +} // namespace std + +#endif // COMPONENTS_PREFS_PREF_VALUE_STORE_H_
diff --git a/src/updater_components/prefs/pref_value_store_unittest.cc b/src/updater_components/prefs/pref_value_store_unittest.cc new file mode 100644 index 0000000..bbbeed8 --- /dev/null +++ b/src/updater_components/prefs/pref_value_store_unittest.cc
@@ -0,0 +1,671 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/pref_value_store.h" + +#include <memory> +#include <string> + +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/values.h" +#include "components/prefs/pref_notifier.h" +#include "components/prefs/testing_pref_store.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Mock; +using testing::_; + +namespace { + +// Allows to capture pref notifications through gmock. +class MockPrefNotifier : public PrefNotifier { + public: + MOCK_METHOD1(OnPreferenceChanged, void(const std::string&)); + MOCK_METHOD1(OnInitializationCompleted, void(bool)); +}; + +// Allows to capture sync model associator interaction. +class MockPrefModelAssociator { + public: + MOCK_METHOD1(ProcessPrefChange, void(const std::string&)); +}; + +} // namespace + +// Names of the preferences used in this test. +namespace prefs { +const char kManagedPref[] = "this.pref.managed"; +const char kSupervisedUserPref[] = "this.pref.supervised_user"; +const char kCommandLinePref[] = "this.pref.command_line"; +const char kExtensionPref[] = "this.pref.extension"; +const char kUserPref[] = "this.pref.user"; +const char kRecommendedPref[] = "this.pref.recommended"; +const char kDefaultPref[] = "this.pref.default"; +const char kMissingPref[] = "this.pref.does_not_exist"; +} + +// Potentially expected values of all preferences used in this test program. +namespace managed_pref { +const char kManagedValue[] = "managed:managed"; +} + +namespace supervised_user_pref { +const char kManagedValue[] = "supervised_user:managed"; +const char kSupervisedUserValue[] = "supervised_user:supervised_user"; +} + +namespace extension_pref { +const char kManagedValue[] = "extension:managed"; +const char kSupervisedUserValue[] = "extension:supervised_user"; +const char kExtensionValue[] = "extension:extension"; +} + +namespace command_line_pref { +const char kManagedValue[] = "command_line:managed"; +const char kSupervisedUserValue[] = "command_line:supervised_user"; +const char kExtensionValue[] = "command_line:extension"; +const char kCommandLineValue[] = "command_line:command_line"; +} + +namespace user_pref { +const char kManagedValue[] = "user:managed"; +const char kSupervisedUserValue[] = "supervised_user:supervised_user"; +const char kExtensionValue[] = "user:extension"; +const char kCommandLineValue[] = "user:command_line"; +const char kUserValue[] = "user:user"; +} + +namespace recommended_pref { +const char kManagedValue[] = "recommended:managed"; +const char kSupervisedUserValue[] = "recommended:supervised_user"; +const char kExtensionValue[] = "recommended:extension"; +const char kCommandLineValue[] = "recommended:command_line"; +const char kUserValue[] = "recommended:user"; +const char kRecommendedValue[] = "recommended:recommended"; +} + +namespace default_pref { +const char kManagedValue[] = "default:managed"; +const char kSupervisedUserValue[] = "default:supervised_user"; +const char kExtensionValue[] = "default:extension"; +const char kCommandLineValue[] = "default:command_line"; +const char kUserValue[] = "default:user"; +const char kRecommendedValue[] = "default:recommended"; +const char kDefaultValue[] = "default:default"; +} + +class PrefValueStoreTest : public testing::Test { + protected: + void SetUp() override { + // Create TestingPrefStores. + CreateManagedPrefs(); + CreateSupervisedUserPrefs(); + CreateExtensionPrefs(); + CreateCommandLinePrefs(); + CreateUserPrefs(); + CreateRecommendedPrefs(); + CreateDefaultPrefs(); + sync_associator_.reset(new MockPrefModelAssociator()); + + // Create a fresh PrefValueStore. + pref_value_store_.reset( + new PrefValueStore(managed_pref_store_.get(), + supervised_user_pref_store_.get(), + extension_pref_store_.get(), + command_line_pref_store_.get(), + user_pref_store_.get(), + recommended_pref_store_.get(), + default_pref_store_.get(), + &pref_notifier_)); + + pref_value_store_->set_callback( + base::Bind(&MockPrefModelAssociator::ProcessPrefChange, + base::Unretained(sync_associator_.get()))); + } + + void CreateManagedPrefs() { + managed_pref_store_ = new TestingPrefStore; + managed_pref_store_->SetString( + prefs::kManagedPref, + managed_pref::kManagedValue); + } + + void CreateSupervisedUserPrefs() { + supervised_user_pref_store_ = new TestingPrefStore; + supervised_user_pref_store_->SetString( + prefs::kManagedPref, + supervised_user_pref::kManagedValue); + supervised_user_pref_store_->SetString( + prefs::kSupervisedUserPref, + supervised_user_pref::kSupervisedUserValue); + } + + void CreateExtensionPrefs() { + extension_pref_store_ = new TestingPrefStore; + extension_pref_store_->SetString( + prefs::kManagedPref, + extension_pref::kManagedValue); + extension_pref_store_->SetString( + prefs::kSupervisedUserPref, + extension_pref::kSupervisedUserValue); + extension_pref_store_->SetString( + prefs::kExtensionPref, + extension_pref::kExtensionValue); + } + + void CreateCommandLinePrefs() { + command_line_pref_store_ = new TestingPrefStore; + command_line_pref_store_->SetString( + prefs::kManagedPref, + command_line_pref::kManagedValue); + command_line_pref_store_->SetString( + prefs::kSupervisedUserPref, + command_line_pref::kSupervisedUserValue); + command_line_pref_store_->SetString( + prefs::kExtensionPref, + command_line_pref::kExtensionValue); + command_line_pref_store_->SetString( + prefs::kCommandLinePref, + command_line_pref::kCommandLineValue); + } + + void CreateUserPrefs() { + user_pref_store_ = new TestingPrefStore; + user_pref_store_->SetString( + prefs::kManagedPref, + user_pref::kManagedValue); + user_pref_store_->SetString( + prefs::kSupervisedUserPref, + user_pref::kSupervisedUserValue); + user_pref_store_->SetString( + prefs::kCommandLinePref, + user_pref::kCommandLineValue); + user_pref_store_->SetString( + prefs::kExtensionPref, + user_pref::kExtensionValue); + user_pref_store_->SetString( + prefs::kUserPref, + user_pref::kUserValue); + } + + void CreateRecommendedPrefs() { + recommended_pref_store_ = new TestingPrefStore; + recommended_pref_store_->SetString( + prefs::kManagedPref, + recommended_pref::kManagedValue); + recommended_pref_store_->SetString( + prefs::kSupervisedUserPref, + recommended_pref::kSupervisedUserValue); + recommended_pref_store_->SetString( + prefs::kCommandLinePref, + recommended_pref::kCommandLineValue); + recommended_pref_store_->SetString( + prefs::kExtensionPref, + recommended_pref::kExtensionValue); + recommended_pref_store_->SetString( + prefs::kUserPref, + recommended_pref::kUserValue); + recommended_pref_store_->SetString( + prefs::kRecommendedPref, + recommended_pref::kRecommendedValue); + } + + void CreateDefaultPrefs() { + default_pref_store_ = new TestingPrefStore; + default_pref_store_->SetString( + prefs::kSupervisedUserPref, + default_pref::kSupervisedUserValue); + default_pref_store_->SetString( + prefs::kManagedPref, + default_pref::kManagedValue); + default_pref_store_->SetString( + prefs::kCommandLinePref, + default_pref::kCommandLineValue); + default_pref_store_->SetString( + prefs::kExtensionPref, + default_pref::kExtensionValue); + default_pref_store_->SetString( + prefs::kUserPref, + default_pref::kUserValue); + default_pref_store_->SetString( + prefs::kRecommendedPref, + default_pref::kRecommendedValue); + default_pref_store_->SetString( + prefs::kDefaultPref, + default_pref::kDefaultValue); + } + + void ExpectValueChangeNotifications(const std::string& name) { + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(name)); + EXPECT_CALL(*sync_associator_, ProcessPrefChange(name)); + } + + void CheckAndClearValueChangeNotifications() { + Mock::VerifyAndClearExpectations(&pref_notifier_); + Mock::VerifyAndClearExpectations(sync_associator_.get()); + } + + MockPrefNotifier pref_notifier_; + std::unique_ptr<MockPrefModelAssociator> sync_associator_; + std::unique_ptr<PrefValueStore> pref_value_store_; + + scoped_refptr<TestingPrefStore> managed_pref_store_; + scoped_refptr<TestingPrefStore> supervised_user_pref_store_; + scoped_refptr<TestingPrefStore> extension_pref_store_; + scoped_refptr<TestingPrefStore> command_line_pref_store_; + scoped_refptr<TestingPrefStore> user_pref_store_; + scoped_refptr<TestingPrefStore> recommended_pref_store_; + scoped_refptr<TestingPrefStore> default_pref_store_; +}; + +TEST_F(PrefValueStoreTest, GetValue) { + const base::Value* value; + + // The following tests read a value from the PrefService. The preferences are + // set in a way such that all lower-priority stores have a value and we can + // test whether overrides work correctly. + + // Test getting a managed value. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kManagedPref, + base::Value::Type::STRING, &value)); + std::string actual_str_value; + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(managed_pref::kManagedValue, actual_str_value); + + // Test getting a supervised user value. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kSupervisedUserPref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(supervised_user_pref::kSupervisedUserValue, actual_str_value); + + // Test getting an extension value. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kExtensionPref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(extension_pref::kExtensionValue, actual_str_value); + + // Test getting a command-line value. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kCommandLinePref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(command_line_pref::kCommandLineValue, actual_str_value); + + // Test getting a user-set value. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kUserPref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(user_pref::kUserValue, actual_str_value); + + // Test getting a user set value overwriting a recommended value. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kRecommendedPref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kRecommendedValue, + actual_str_value); + + // Test getting a default value. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kDefaultPref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(default_pref::kDefaultValue, actual_str_value); + + // Test getting a preference value that the |PrefValueStore| + // does not contain. + base::Value tmp_dummy_value(true); + value = &tmp_dummy_value; + ASSERT_FALSE(pref_value_store_->GetValue(prefs::kMissingPref, + base::Value::Type::STRING, &value)); + ASSERT_FALSE(value); +} + +TEST_F(PrefValueStoreTest, GetRecommendedValue) { + const base::Value* value; + + // The following tests read a value from the PrefService. The preferences are + // set in a way such that all lower-priority stores have a value and we can + // test whether overrides do not clutter the recommended value. + + // Test getting recommended value when a managed value is present. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kManagedPref, + base::Value::Type::STRING, &value)); + std::string actual_str_value; + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kManagedValue, actual_str_value); + + // Test getting recommended value when a supervised user value is present. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kSupervisedUserPref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kSupervisedUserValue, actual_str_value); + + // Test getting recommended value when an extension value is present. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kExtensionPref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kExtensionValue, actual_str_value); + + // Test getting recommended value when a command-line value is present. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kCommandLinePref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kCommandLineValue, actual_str_value); + + // Test getting recommended value when a user-set value is present. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kUserPref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kUserValue, actual_str_value); + + // Test getting recommended value when no higher-priority value is present. + value = nullptr; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kRecommendedPref, + base::Value::Type::STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kRecommendedValue, + actual_str_value); + + // Test getting recommended value when no recommended value is present. + base::Value tmp_dummy_value(true); + value = &tmp_dummy_value; + ASSERT_FALSE(pref_value_store_->GetRecommendedValue( + prefs::kDefaultPref, + base::Value::Type::STRING, &value)); + ASSERT_FALSE(value); + + // Test getting a preference value that the |PrefValueStore| + // does not contain. + value = &tmp_dummy_value; + ASSERT_FALSE(pref_value_store_->GetRecommendedValue( + prefs::kMissingPref, + base::Value::Type::STRING, &value)); + ASSERT_FALSE(value); +} + +TEST_F(PrefValueStoreTest, PrefChanges) { + // Check pref controlled by highest-priority store. + ExpectValueChangeNotifications(prefs::kManagedPref); + managed_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + supervised_user_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + extension_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + command_line_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + user_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + recommended_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + default_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + // Check pref controlled by user store. + ExpectValueChangeNotifications(prefs::kUserPref); + managed_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + extension_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + command_line_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + user_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + recommended_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + default_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + // Check pref controlled by default-pref store. + ExpectValueChangeNotifications(prefs::kDefaultPref); + managed_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + extension_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + command_line_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + user_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + recommended_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + default_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); +} + +TEST_F(PrefValueStoreTest, OnInitializationCompleted) { + EXPECT_CALL(pref_notifier_, OnInitializationCompleted(true)).Times(0); + managed_pref_store_->SetInitializationCompleted(); + supervised_user_pref_store_->SetInitializationCompleted(); + extension_pref_store_->SetInitializationCompleted(); + command_line_pref_store_->SetInitializationCompleted(); + recommended_pref_store_->SetInitializationCompleted(); + default_pref_store_->SetInitializationCompleted(); + Mock::VerifyAndClearExpectations(&pref_notifier_); + + // The notification should only be triggered after the last store is done. + EXPECT_CALL(pref_notifier_, OnInitializationCompleted(true)).Times(1); + user_pref_store_->SetInitializationCompleted(); + Mock::VerifyAndClearExpectations(&pref_notifier_); +} + +TEST_F(PrefValueStoreTest, PrefValueInManagedStore) { + EXPECT_TRUE(pref_value_store_->PrefValueInManagedStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueInExtensionStore) { + EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore( + prefs::kManagedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore( + prefs::kSupervisedUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueInUserStore) { + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kManagedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kSupervisedUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kExtensionPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kCommandLinePref)); + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInUserStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInUserStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInUserStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueFromExtensionStore) { + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kSupervisedUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueFromUserStore) { + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kCommandLinePref)); + EXPECT_TRUE(pref_value_store_->PrefValueFromUserStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueFromRecommendedStore) { + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueFromDefaultStore) { + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kRecommendedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueUserModifiable) { + EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( + prefs::kCommandLinePref)); + EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable( + prefs::kUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable( + prefs::kRecommendedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable( + prefs::kDefaultPref)); + EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueExtensionModifiable) { + EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kSupervisedUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kExtensionPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kCommandLinePref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kRecommendedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kDefaultPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kMissingPref)); +}
diff --git a/src/updater_components/prefs/prefs_export.h b/src/updater_components/prefs/prefs_export.h new file mode 100644 index 0000000..e047ad9 --- /dev/null +++ b/src/updater_components/prefs/prefs_export.h
@@ -0,0 +1,29 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_ +#define COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(COMPONENTS_PREFS_IMPLEMENTATION) +#define COMPONENTS_PREFS_EXPORT __declspec(dllexport) +#else +#define COMPONENTS_PREFS_EXPORT __declspec(dllimport) +#endif // defined(COMPONENTS_PREFS_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(COMPONENTS_PREFS_IMPLEMENTATION) +#define COMPONENTS_PREFS_EXPORT __attribute__((visibility("default"))) +#else +#define COMPONENTS_PREFS_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define COMPONENTS_PREFS_EXPORT +#endif + +#endif // COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_
diff --git a/src/updater_components/prefs/scoped_user_pref_update.cc b/src/updater_components/prefs/scoped_user_pref_update.cc new file mode 100644 index 0000000..27d56af --- /dev/null +++ b/src/updater_components/prefs/scoped_user_pref_update.cc
@@ -0,0 +1,44 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/scoped_user_pref_update.h" + +#include "base/logging.h" +#include "components/prefs/pref_notifier.h" +#include "components/prefs/pref_service.h" + +namespace subtle { + +ScopedUserPrefUpdateBase::ScopedUserPrefUpdateBase(PrefService* service, + const std::string& path) + : service_(service), path_(path), value_(nullptr) { + DCHECK_CALLED_ON_VALID_SEQUENCE(service_->sequence_checker_); +} + +ScopedUserPrefUpdateBase::~ScopedUserPrefUpdateBase() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + Notify(); +} + +base::Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!value_) + value_ = service_->GetMutableUserPref(path_, type); + + // |value_| might be downcast to base::DictionaryValue or base::ListValue, + // side-stepping CHECKs built into base::Value. Thus we need to be certain + // that the type matches. + if (value_) + CHECK_EQ(value_->type(), type); + return value_; +} + +void ScopedUserPrefUpdateBase::Notify() { + if (value_) { + service_->ReportUserPrefChanged(path_); + value_ = nullptr; + } +} + +} // namespace subtle
diff --git a/src/updater_components/prefs/scoped_user_pref_update.h b/src/updater_components/prefs/scoped_user_pref_update.h new file mode 100644 index 0000000..7de6bfb --- /dev/null +++ b/src/updater_components/prefs/scoped_user_pref_update.h
@@ -0,0 +1,110 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A helper class that assists preferences in firing notifications when lists +// or dictionaries are changed. + +#ifndef COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_ +#define COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_ + +#include <string> + +#include "base/macros.h" +#include "base/sequence_checker.h" +#include "base/values.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/prefs_export.h" + +class PrefService; + +namespace base { +class DictionaryValue; +class ListValue; +} + +namespace subtle { + +// Base class for ScopedUserPrefUpdateTemplate that contains the parts +// that do not depend on ScopedUserPrefUpdateTemplate's template parameter. +// +// We need this base class mostly for making it a friend of PrefService +// and getting access to PrefService::GetMutableUserPref and +// PrefService::ReportUserPrefChanged. +class COMPONENTS_PREFS_EXPORT ScopedUserPrefUpdateBase { + protected: + ScopedUserPrefUpdateBase(PrefService* service, const std::string& path); + + // Calls Notify(). + ~ScopedUserPrefUpdateBase(); + + // Sets |value_| to |service_|->GetMutableUserPref and returns it. + base::Value* GetValueOfType(base::Value::Type type); + + private: + // If |value_| is not null, triggers a notification of PrefObservers and + // resets |value_|. + void Notify(); + + // Weak pointer. + PrefService* service_; + // Path of the preference being updated. + std::string path_; + // Cache of value from user pref store (set between Get() and Notify() calls). + base::Value* value_; + + SEQUENCE_CHECKER(sequence_checker_); + + DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdateBase); +}; + +} // namespace subtle + +// Class to support modifications to DictionaryValues and ListValues while +// guaranteeing that PrefObservers are notified of changed values. +// +// This class may only be used on the UI thread as it requires access to the +// PrefService. +template <typename T, base::Value::Type type_enum_value> +class ScopedUserPrefUpdate : public subtle::ScopedUserPrefUpdateBase { + public: + ScopedUserPrefUpdate(PrefService* service, const std::string& path) + : ScopedUserPrefUpdateBase(service, path) {} + + // Triggers an update notification if Get() was called. + virtual ~ScopedUserPrefUpdate() {} + + // Returns a mutable |T| instance that + // - is already in the user pref store, or + // - is (silently) created and written to the user pref store if none existed + // before. + // + // Calling Get() implies that an update notification is necessary at + // destruction time. + // + // The ownership of the return value remains with the user pref store. + // Virtual so it can be overriden in subclasses that transform the value + // before returning it (for example to return a subelement of a dictionary). + virtual T* Get() { + return static_cast<T*>(GetValueOfType(type_enum_value)); + } + + T& operator*() { + return *Get(); + } + + T* operator->() { + return Get(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdate); +}; + +typedef ScopedUserPrefUpdate<base::DictionaryValue, + base::Value::Type::DICTIONARY> + DictionaryPrefUpdate; +typedef ScopedUserPrefUpdate<base::ListValue, base::Value::Type::LIST> + ListPrefUpdate; + +#endif // COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
diff --git a/src/updater_components/prefs/scoped_user_pref_update_unittest.cc b/src/updater_components/prefs/scoped_user_pref_update_unittest.cc new file mode 100644 index 0000000..fa1dc71 --- /dev/null +++ b/src/updater_components/prefs/scoped_user_pref_update_unittest.cc
@@ -0,0 +1,110 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/mock_pref_change_callback.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Mock; + +class ScopedUserPrefUpdateTest : public testing::Test { + public: + ScopedUserPrefUpdateTest() : observer_(&prefs_) {} + ~ScopedUserPrefUpdateTest() override {} + + protected: + void SetUp() override { + prefs_.registry()->RegisterDictionaryPref(kPref); + registrar_.Init(&prefs_); + registrar_.Add(kPref, observer_.GetCallback()); + } + + static const char kPref[]; + static const char kKey[]; + static const char kValue[]; + + TestingPrefServiceSimple prefs_; + MockPrefChangeCallback observer_; + PrefChangeRegistrar registrar_; +}; + +const char ScopedUserPrefUpdateTest::kPref[] = "name"; +const char ScopedUserPrefUpdateTest::kKey[] = "key"; +const char ScopedUserPrefUpdateTest::kValue[] = "value"; + +TEST_F(ScopedUserPrefUpdateTest, RegularUse) { + // Dictionary that will be expected to be set at the end. + base::DictionaryValue expected_dictionary; + expected_dictionary.SetString(kKey, kValue); + + { + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + DictionaryPrefUpdate update(&prefs_, kPref); + base::DictionaryValue* value = update.Get(); + ASSERT_TRUE(value); + value->SetString(kKey, kValue); + + // The dictionary was created for us but the creation should have happened + // silently without notifications. + Mock::VerifyAndClearExpectations(&observer_); + + // Modifications happen online and are instantly visible, though. + const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref); + ASSERT_TRUE(current_value); + EXPECT_TRUE(expected_dictionary.Equals(current_value)); + + // Now we are leaving the scope of the update so we should be notified. + observer_.Expect(kPref, &expected_dictionary); + } + Mock::VerifyAndClearExpectations(&observer_); + + const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref); + ASSERT_TRUE(current_value); + EXPECT_TRUE(expected_dictionary.Equals(current_value)); +} + +TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnything) { + const base::DictionaryValue* old_value = prefs_.GetDictionary(kPref); + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + { + DictionaryPrefUpdate update(&prefs_, kPref); + } + const base::DictionaryValue* new_value = prefs_.GetDictionary(kPref); + EXPECT_EQ(old_value, new_value); + Mock::VerifyAndClearExpectations(&observer_); +} + +TEST_F(ScopedUserPrefUpdateTest, UpdatingListPrefWithDefaults) { + base::Value::ListStorage defaults; + defaults.emplace_back("firstvalue"); + defaults.emplace_back("secondvalue"); + + std::string pref_name = "mypref"; + prefs_.registry()->RegisterListPref(pref_name, + base::Value(std::move(defaults))); + EXPECT_EQ(2u, prefs_.GetList(pref_name)->GetList().size()); + + ListPrefUpdate update(&prefs_, pref_name); + update->AppendString("thirdvalue"); + EXPECT_EQ(3u, prefs_.GetList(pref_name)->GetList().size()); +} + +TEST_F(ScopedUserPrefUpdateTest, UpdatingDictionaryPrefWithDefaults) { + base::Value defaults(base::Value::Type::DICTIONARY); + defaults.SetKey("firstkey", base::Value("value")); + defaults.SetKey("secondkey", base::Value("value")); + + std::string pref_name = "mypref"; + prefs_.registry()->RegisterDictionaryPref(pref_name, std::move(defaults)); + EXPECT_EQ(2u, prefs_.GetDictionary(pref_name)->size()); + + DictionaryPrefUpdate update(&prefs_, pref_name); + update->SetKey("thirdkey", base::Value("value")); + EXPECT_EQ(3u, prefs_.GetDictionary(pref_name)->size()); +}
diff --git a/src/updater_components/prefs/testing_pref_service.cc b/src/updater_components/prefs/testing_pref_service.cc new file mode 100644 index 0000000..aa6582d --- /dev/null +++ b/src/updater_components/prefs/testing_pref_service.cc
@@ -0,0 +1,59 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/testing_pref_service.h" + +#include <memory> + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "components/prefs/default_pref_store.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_value_store.h" +#include "testing/gtest/include/gtest/gtest.h" + +template <> +TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase( + TestingPrefStore* managed_prefs, + TestingPrefStore* extension_prefs, + TestingPrefStore* user_prefs, + TestingPrefStore* recommended_prefs, + PrefRegistry* pref_registry, + PrefNotifierImpl* pref_notifier) + : PrefService( + std::unique_ptr<PrefNotifierImpl>(pref_notifier), + std::make_unique<PrefValueStore>(managed_prefs, + nullptr, + extension_prefs, + nullptr, + user_prefs, + recommended_prefs, + pref_registry->defaults().get(), + pref_notifier), + user_prefs, + pref_registry, + base::Bind(&TestingPrefServiceBase<PrefService, + PrefRegistry>::HandleReadError), + false), + managed_prefs_(managed_prefs), + extension_prefs_(extension_prefs), + user_prefs_(user_prefs), + recommended_prefs_(recommended_prefs) {} + +TestingPrefServiceSimple::TestingPrefServiceSimple() + : TestingPrefServiceBase<PrefService, PrefRegistry>( + new TestingPrefStore(), + new TestingPrefStore(), + new TestingPrefStore(), + new TestingPrefStore(), + new PrefRegistrySimple(), + new PrefNotifierImpl()) {} + +TestingPrefServiceSimple::~TestingPrefServiceSimple() { +} + +PrefRegistrySimple* TestingPrefServiceSimple::registry() { + return static_cast<PrefRegistrySimple*>(DeprecatedGetPrefRegistry()); +}
diff --git a/src/updater_components/prefs/testing_pref_service.h b/src/updater_components/prefs/testing_pref_service.h new file mode 100644 index 0000000..f10e0cb --- /dev/null +++ b/src/updater_components/prefs/testing_pref_service.h
@@ -0,0 +1,233 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_ +#define COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_ + +#include <memory> +#include <utility> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/prefs/pref_registry.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/testing_pref_store.h" + +class PrefNotifierImpl; +class PrefRegistrySimple; +class TestingPrefStore; + +// A PrefService subclass for testing. It operates totally in memory and +// provides additional API for manipulating preferences at the different levels +// (managed, extension, user) conveniently. +// +// Use this via its specializations, e.g. TestingPrefServiceSimple. +template <class SuperPrefService, class ConstructionPrefRegistry> +class TestingPrefServiceBase : public SuperPrefService { + public: + virtual ~TestingPrefServiceBase(); + + // Reads the value of a preference from the managed layer. Returns NULL if the + // preference is not defined at the managed layer. + const base::Value* GetManagedPref(const std::string& path) const; + + // Sets a preference on the managed layer and fires observers if the + // preference changed. + void SetManagedPref(const std::string& path, + std::unique_ptr<base::Value> value); + + // Clears the preference on the managed layer and fire observers if the + // preference has been defined previously. + void RemoveManagedPref(const std::string& path); + + // Similar to the above, but for extension preferences. + // Does not really know about extensions and their order of installation. + // Useful in tests that only check that a preference is overridden by an + // extension. + const base::Value* GetExtensionPref(const std::string& path) const; + void SetExtensionPref(const std::string& path, + std::unique_ptr<base::Value> value); + void RemoveExtensionPref(const std::string& path); + + // Similar to the above, but for user preferences. + const base::Value* GetUserPref(const std::string& path) const; + void SetUserPref(const std::string& path, std::unique_ptr<base::Value> value); + void RemoveUserPref(const std::string& path); + + // Similar to the above, but for recommended policy preferences. + const base::Value* GetRecommendedPref(const std::string& path) const; + void SetRecommendedPref(const std::string& path, + std::unique_ptr<base::Value> value); + void RemoveRecommendedPref(const std::string& path); + + // Do-nothing implementation for TestingPrefService. + static void HandleReadError(PersistentPrefStore::PrefReadError error) {} + + protected: + TestingPrefServiceBase(TestingPrefStore* managed_prefs, + TestingPrefStore* extension_prefs, + TestingPrefStore* user_prefs, + TestingPrefStore* recommended_prefs, + ConstructionPrefRegistry* pref_registry, + PrefNotifierImpl* pref_notifier); + + private: + // Reads the value of the preference indicated by |path| from |pref_store|. + // Returns NULL if the preference was not found. + const base::Value* GetPref(TestingPrefStore* pref_store, + const std::string& path) const; + + // Sets the value for |path| in |pref_store|. + void SetPref(TestingPrefStore* pref_store, + const std::string& path, + std::unique_ptr<base::Value> value); + + // Removes the preference identified by |path| from |pref_store|. + void RemovePref(TestingPrefStore* pref_store, const std::string& path); + + // Pointers to the pref stores our value store uses. + scoped_refptr<TestingPrefStore> managed_prefs_; + scoped_refptr<TestingPrefStore> extension_prefs_; + scoped_refptr<TestingPrefStore> user_prefs_; + scoped_refptr<TestingPrefStore> recommended_prefs_; + + DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceBase); +}; + +// Test version of PrefService. +class TestingPrefServiceSimple + : public TestingPrefServiceBase<PrefService, PrefRegistry> { + public: + TestingPrefServiceSimple(); + ~TestingPrefServiceSimple() override; + + // This is provided as a convenience for registering preferences on + // an existing TestingPrefServiceSimple instance. On a production + // PrefService you would do all registrations before constructing + // it, passing it a PrefRegistry via its constructor (or via + // e.g. PrefServiceFactory). + PrefRegistrySimple* registry(); + + private: + DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceSimple); +}; + +template<> +TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase( + TestingPrefStore* managed_prefs, + TestingPrefStore* extension_prefs, + TestingPrefStore* user_prefs, + TestingPrefStore* recommended_prefs, + PrefRegistry* pref_registry, + PrefNotifierImpl* pref_notifier); + +template<class SuperPrefService, class ConstructionPrefRegistry> +TestingPrefServiceBase< + SuperPrefService, ConstructionPrefRegistry>::~TestingPrefServiceBase() { +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* TestingPrefServiceBase< + SuperPrefService, + ConstructionPrefRegistry>::GetManagedPref(const std::string& path) const { + return GetPref(managed_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetManagedPref(const std::string& path, + std::unique_ptr<base::Value> value) { + SetPref(managed_prefs_.get(), path, std::move(value)); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveManagedPref(const std::string& path) { + RemovePref(managed_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* TestingPrefServiceBase< + SuperPrefService, + ConstructionPrefRegistry>::GetExtensionPref(const std::string& path) const { + return GetPref(extension_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetExtensionPref(const std::string& path, + std::unique_ptr<base::Value> value) { + SetPref(extension_prefs_.get(), path, std::move(value)); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveExtensionPref(const std::string& path) { + RemovePref(extension_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* +TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetUserPref( + const std::string& path) const { + return GetPref(user_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetUserPref(const std::string& path, std::unique_ptr<base::Value> value) { + SetPref(user_prefs_.get(), path, std::move(value)); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveUserPref(const std::string& path) { + RemovePref(user_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* +TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + GetRecommendedPref(const std::string& path) const { + return GetPref(recommended_prefs_, path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetRecommendedPref(const std::string& path, + std::unique_ptr<base::Value> value) { + SetPref(recommended_prefs_.get(), path, std::move(value)); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveRecommendedPref(const std::string& path) { + RemovePref(recommended_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* +TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetPref( + TestingPrefStore* pref_store, + const std::string& path) const { + const base::Value* res; + return pref_store->GetValue(path, &res) ? res : NULL; +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetPref(TestingPrefStore* pref_store, + const std::string& path, + std::unique_ptr<base::Value> value) { + pref_store->SetValue(path, std::move(value), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemovePref(TestingPrefStore* pref_store, const std::string& path) { + pref_store->RemoveValue(path, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); +} + +#endif // COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_
diff --git a/src/updater_components/prefs/testing_pref_store.cc b/src/updater_components/prefs/testing_pref_store.cc new file mode 100644 index 0000000..4380407 --- /dev/null +++ b/src/updater_components/prefs/testing_pref_store.cc
@@ -0,0 +1,216 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/testing_pref_store.h" + +#include <memory> +#include <utility> + +#include "base/json/json_writer.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/values.h" +#include "testing/gtest/include/gtest/gtest.h" + +TestingPrefStore::TestingPrefStore() + : read_only_(true), + read_success_(true), + read_error_(PersistentPrefStore::PREF_READ_ERROR_NONE), + block_async_read_(false), + pending_async_read_(false), + init_complete_(false), + committed_(true) {} + +bool TestingPrefStore::GetValue(const std::string& key, + const base::Value** value) const { + return prefs_.GetValue(key, value); +} + +std::unique_ptr<base::DictionaryValue> TestingPrefStore::GetValues() const { + return prefs_.AsDictionaryValue(); +} + +bool TestingPrefStore::GetMutableValue(const std::string& key, + base::Value** value) { + return prefs_.GetValue(key, value); +} + +void TestingPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void TestingPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool TestingPrefStore::HasObservers() const { + return observers_.might_have_observers(); +} + +bool TestingPrefStore::IsInitializationComplete() const { + return init_complete_; +} + +void TestingPrefStore::SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + DCHECK(value); + if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)))) { + committed_ = false; + NotifyPrefValueChanged(key); + } +} + +void TestingPrefStore::SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + DCHECK(value); + CheckPrefIsSerializable(key, *value); + if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)))) + committed_ = false; +} + +void TestingPrefStore::RemoveValue(const std::string& key, uint32_t flags) { + if (prefs_.RemoveValue(key)) { + committed_ = false; + NotifyPrefValueChanged(key); + } +} + +bool TestingPrefStore::ReadOnly() const { + return read_only_; +} + +PersistentPrefStore::PrefReadError TestingPrefStore::GetReadError() const { + return read_error_; +} + +PersistentPrefStore::PrefReadError TestingPrefStore::ReadPrefs() { + NotifyInitializationCompleted(); + return read_error_; +} + +void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { + DCHECK(!pending_async_read_); + error_delegate_.reset(error_delegate); + if (block_async_read_) + pending_async_read_ = true; + else + NotifyInitializationCompleted(); +} + +void TestingPrefStore::CommitPendingWrite( + base::OnceClosure reply_callback, + base::OnceClosure synchronous_done_callback) { + committed_ = true; + PersistentPrefStore::CommitPendingWrite(std::move(reply_callback), + std::move(synchronous_done_callback)); +} + +void TestingPrefStore::SchedulePendingLossyWrites() {} + +void TestingPrefStore::SetInitializationCompleted() { + NotifyInitializationCompleted(); +} + +void TestingPrefStore::NotifyPrefValueChanged(const std::string& key) { + for (Observer& observer : observers_) + observer.OnPrefValueChanged(key); +} + +void TestingPrefStore::NotifyInitializationCompleted() { + DCHECK(!init_complete_); + init_complete_ = true; + if (read_success_ && read_error_ != PREF_READ_ERROR_NONE && error_delegate_) + error_delegate_->OnError(read_error_); + for (Observer& observer : observers_) + observer.OnInitializationCompleted(read_success_); +} + +void TestingPrefStore::ReportValueChanged(const std::string& key, + uint32_t flags) { + const base::Value* value = nullptr; + if (prefs_.GetValue(key, &value)) + CheckPrefIsSerializable(key, *value); + + for (Observer& observer : observers_) + observer.OnPrefValueChanged(key); +} + +void TestingPrefStore::SetString(const std::string& key, + const std::string& value) { + SetValue(key, std::make_unique<base::Value>(value), DEFAULT_PREF_WRITE_FLAGS); +} + +void TestingPrefStore::SetInteger(const std::string& key, int value) { + SetValue(key, std::make_unique<base::Value>(value), DEFAULT_PREF_WRITE_FLAGS); +} + +void TestingPrefStore::SetBoolean(const std::string& key, bool value) { + SetValue(key, std::make_unique<base::Value>(value), DEFAULT_PREF_WRITE_FLAGS); +} + +bool TestingPrefStore::GetString(const std::string& key, + std::string* value) const { + const base::Value* stored_value; + if (!prefs_.GetValue(key, &stored_value) || !stored_value) + return false; + + return stored_value->GetAsString(value); +} + +bool TestingPrefStore::GetInteger(const std::string& key, int* value) const { + const base::Value* stored_value; + if (!prefs_.GetValue(key, &stored_value) || !stored_value) + return false; + + return stored_value->GetAsInteger(value); +} + +bool TestingPrefStore::GetBoolean(const std::string& key, bool* value) const { + const base::Value* stored_value; + if (!prefs_.GetValue(key, &stored_value) || !stored_value) + return false; + + return stored_value->GetAsBoolean(value); +} + +void TestingPrefStore::SetBlockAsyncRead(bool block_async_read) { + DCHECK(!init_complete_); + block_async_read_ = block_async_read; + if (pending_async_read_ && !block_async_read_) + NotifyInitializationCompleted(); +} + +void TestingPrefStore::ClearMutableValues() { + NOTIMPLEMENTED(); +} + +void TestingPrefStore::OnStoreDeletionFromDisk() {} + +void TestingPrefStore::set_read_only(bool read_only) { + read_only_ = read_only; +} + +void TestingPrefStore::set_read_success(bool read_success) { + DCHECK(!init_complete_); + read_success_ = read_success; +} + +void TestingPrefStore::set_read_error( + PersistentPrefStore::PrefReadError read_error) { + DCHECK(!init_complete_); + read_error_ = read_error; +} + +TestingPrefStore::~TestingPrefStore() { + for (auto& pref : prefs_) + CheckPrefIsSerializable(pref.first, pref.second); +} + +void TestingPrefStore::CheckPrefIsSerializable(const std::string& key, + const base::Value& value) { + std::string json; + EXPECT_TRUE(base::JSONWriter::Write(value, &json)) + << "Pref \"" << key << "\" is not serializable as JSON."; +}
diff --git a/src/updater_components/prefs/testing_pref_store.h b/src/updater_components/prefs/testing_pref_store.h new file mode 100644 index 0000000..499b094 --- /dev/null +++ b/src/updater_components/prefs/testing_pref_store.h
@@ -0,0 +1,122 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_TESTING_PREF_STORE_H_ +#define COMPONENTS_PREFS_TESTING_PREF_STORE_H_ + +#include <stdint.h> + +#include <string> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/observer_list.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/prefs/pref_value_map.h" + +// |TestingPrefStore| is a preference store implementation that allows tests to +// explicitly manipulate the contents of the store, triggering notifications +// where appropriate. +class TestingPrefStore : public PersistentPrefStore { + public: + TestingPrefStore(); + + // Overriden from PrefStore. + bool GetValue(const std::string& key, + const base::Value** result) const override; + std::unique_ptr<base::DictionaryValue> GetValues() const override; + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + bool IsInitializationComplete() const override; + + // PersistentPrefStore overrides: + bool GetMutableValue(const std::string& key, base::Value** result) override; + void ReportValueChanged(const std::string& key, uint32_t flags) override; + void SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + void SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + void RemoveValue(const std::string& key, uint32_t flags) override; + bool ReadOnly() const override; + PrefReadError GetReadError() const override; + PersistentPrefStore::PrefReadError ReadPrefs() override; + void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; + void CommitPendingWrite(base::OnceClosure reply_callback, + base::OnceClosure synchronous_done_callback) override; + void SchedulePendingLossyWrites() override; + + // Marks the store as having completed initialization. + void SetInitializationCompleted(); + + // Used for tests to trigger notifications explicitly. + void NotifyPrefValueChanged(const std::string& key); + void NotifyInitializationCompleted(); + + // Some convenience getters/setters. + void SetString(const std::string& key, const std::string& value); + void SetInteger(const std::string& key, int value); + void SetBoolean(const std::string& key, bool value); + + bool GetString(const std::string& key, std::string* value) const; + bool GetInteger(const std::string& key, int* value) const; + bool GetBoolean(const std::string& key, bool* value) const; + + // Determines whether ReadPrefsAsync completes immediately. Defaults to false + // (non-blocking). To block, invoke this with true (blocking) before the call + // to ReadPrefsAsync. To unblock, invoke again with false (non-blocking) after + // the call to ReadPrefsAsync. + void SetBlockAsyncRead(bool block_async_read); + + void ClearMutableValues() override; + void OnStoreDeletionFromDisk() override; + + // Getter and Setter methods for setting and getting the state of the + // |TestingPrefStore|. + virtual void set_read_only(bool read_only); + void set_read_success(bool read_success); + void set_read_error(PersistentPrefStore::PrefReadError read_error); + bool committed() { return committed_; } + + protected: + ~TestingPrefStore() override; + + private: + void CheckPrefIsSerializable(const std::string& key, + const base::Value& value); + + // Stores the preference values. + PrefValueMap prefs_; + + // Flag that indicates if the PrefStore is read-only + bool read_only_; + + // The result to pass to PrefStore::Observer::OnInitializationCompleted + bool read_success_; + + // The result to return from ReadPrefs or ReadPrefsAsync. + PersistentPrefStore::PrefReadError read_error_; + + // Whether a call to ReadPrefsAsync should block. + bool block_async_read_; + + // Whether there is a pending call to ReadPrefsAsync. + bool pending_async_read_; + + // Whether initialization has been completed. + bool init_complete_; + + // Whether the store contents have been committed to disk since the last + // mutation. + bool committed_; + + std::unique_ptr<ReadErrorDelegate> error_delegate_; + base::ObserverList<PrefStore::Observer, true>::Unchecked observers_; + + DISALLOW_COPY_AND_ASSIGN(TestingPrefStore); +}; + +#endif // COMPONENTS_PREFS_TESTING_PREF_STORE_H_
diff --git a/src/updater_components/prefs/value_map_pref_store.cc b/src/updater_components/prefs/value_map_pref_store.cc new file mode 100644 index 0000000..79c2017 --- /dev/null +++ b/src/updater_components/prefs/value_map_pref_store.cc
@@ -0,0 +1,76 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/value_map_pref_store.h" + +#include <algorithm> +#include <utility> + +#include "base/stl_util.h" +#include "base/values.h" + +ValueMapPrefStore::ValueMapPrefStore() {} + +bool ValueMapPrefStore::GetValue(const std::string& key, + const base::Value** value) const { + return prefs_.GetValue(key, value); +} + +std::unique_ptr<base::DictionaryValue> ValueMapPrefStore::GetValues() const { + return prefs_.AsDictionaryValue(); +} + +void ValueMapPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void ValueMapPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool ValueMapPrefStore::HasObservers() const { + return observers_.might_have_observers(); +} + +void ValueMapPrefStore::SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + DCHECK(value); + if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)))) { + for (Observer& observer : observers_) + observer.OnPrefValueChanged(key); + } +} + +void ValueMapPrefStore::RemoveValue(const std::string& key, uint32_t flags) { + if (prefs_.RemoveValue(key)) { + for (Observer& observer : observers_) + observer.OnPrefValueChanged(key); + } +} + +bool ValueMapPrefStore::GetMutableValue(const std::string& key, + base::Value** value) { + return prefs_.GetValue(key, value); +} + +void ValueMapPrefStore::ReportValueChanged(const std::string& key, + uint32_t flags) { + for (Observer& observer : observers_) + observer.OnPrefValueChanged(key); +} + +void ValueMapPrefStore::SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) { + DCHECK(value); + prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value))); +} + +ValueMapPrefStore::~ValueMapPrefStore() {} + +void ValueMapPrefStore::NotifyInitializationCompleted() { + for (Observer& observer : observers_) + observer.OnInitializationCompleted(true); +}
diff --git a/src/updater_components/prefs/value_map_pref_store.h b/src/updater_components/prefs/value_map_pref_store.h new file mode 100644 index 0000000..f0de0cd --- /dev/null +++ b/src/updater_components/prefs/value_map_pref_store.h
@@ -0,0 +1,58 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_ +#define COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_ + +#include <stdint.h> + +#include <map> +#include <string> + +#include "base/macros.h" +#include "base/observer_list.h" +#include "components/prefs/pref_value_map.h" +#include "components/prefs/prefs_export.h" +#include "components/prefs/writeable_pref_store.h" + +// A basic PrefStore implementation that uses a simple name-value map for +// storing the preference values. +class COMPONENTS_PREFS_EXPORT ValueMapPrefStore : public WriteablePrefStore { + public: + ValueMapPrefStore(); + + // PrefStore overrides: + bool GetValue(const std::string& key, + const base::Value** value) const override; + std::unique_ptr<base::DictionaryValue> GetValues() const override; + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + + // WriteablePrefStore overrides: + void SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + void RemoveValue(const std::string& key, uint32_t flags) override; + bool GetMutableValue(const std::string& key, base::Value** value) override; + void ReportValueChanged(const std::string& key, uint32_t flags) override; + void SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override; + + protected: + ~ValueMapPrefStore() override; + + // Notify observers about the initialization completed event. + void NotifyInitializationCompleted(); + + private: + PrefValueMap prefs_; + + base::ObserverList<PrefStore::Observer, true>::Unchecked observers_; + + DISALLOW_COPY_AND_ASSIGN(ValueMapPrefStore); +}; + +#endif // COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_
diff --git a/src/updater_components/prefs/writeable_pref_store.cc b/src/updater_components/prefs/writeable_pref_store.cc new file mode 100644 index 0000000..d51985e --- /dev/null +++ b/src/updater_components/prefs/writeable_pref_store.cc
@@ -0,0 +1,14 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/prefs/writeable_pref_store.h" + +void WriteablePrefStore::ReportSubValuesChanged( + const std::string& key, + std::set<std::vector<std::string>> path_components, + uint32_t flags) { + // Default implementation. Subclasses may use |path_components| to improve + // performance. + ReportValueChanged(key, flags); +}
diff --git a/src/updater_components/prefs/writeable_pref_store.h b/src/updater_components/prefs/writeable_pref_store.h new file mode 100644 index 0000000..9a69c7c --- /dev/null +++ b/src/updater_components/prefs/writeable_pref_store.h
@@ -0,0 +1,86 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_ +#define COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_ + +#include <stdint.h> + +#include <memory> +#include <set> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "components/prefs/pref_store.h" + +namespace base { +class Value; +} + +// A pref store that can be written to as well as read from. +class COMPONENTS_PREFS_EXPORT WriteablePrefStore : public PrefStore { + public: + // PrefWriteFlags can be used to change the way a pref will be written to + // storage. + enum PrefWriteFlags : uint32_t { + // No flags are specified. + DEFAULT_PREF_WRITE_FLAGS = 0, + + // This marks the pref as "lossy". There is no strict time guarantee on when + // a lossy pref will be persisted to permanent storage when it is modified. + LOSSY_PREF_WRITE_FLAG = 1 << 1 + }; + + WriteablePrefStore() {} + + // Sets a |value| for |key| in the store. |value| must be non-NULL. |flags| is + // a bitmask of PrefWriteFlags. + virtual void SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) = 0; + + // Removes the value for |key|. + virtual void RemoveValue(const std::string& key, uint32_t flags) = 0; + + // Equivalent to PrefStore::GetValue but returns a mutable value. + virtual bool GetMutableValue(const std::string& key, + base::Value** result) = 0; + + // Triggers a value changed notification. This function or + // ReportSubValuesChanged needs to be called if one retrieves a list or + // dictionary with GetMutableValue and change its value. SetValue takes care + // of notifications itself. Note that ReportValueChanged will trigger + // notifications even if nothing has changed. |flags| is a bitmask of + // PrefWriteFlags. + virtual void ReportValueChanged(const std::string& key, uint32_t flags) = 0; + + // Triggers a value changed notification for |path_components| in the |key| + // pref. This function or ReportValueChanged needs to be called if one + // retrieves a list or dictionary with GetMutableValue and change its value. + // SetValue takes care of notifications itself. Note that + // ReportSubValuesChanged will trigger notifications even if nothing has + // changed. |flags| is a bitmask of PrefWriteFlags. + virtual void ReportSubValuesChanged( + const std::string& key, + std::set<std::vector<std::string>> path_components, + uint32_t flags); + + // Same as SetValue, but doesn't generate notifications. This is used by + // PrefService::GetMutableUserPref() in order to put empty entries + // into the user pref store. Using SetValue is not an option since existing + // tests rely on the number of notifications generated. |flags| is a bitmask + // of PrefWriteFlags. + virtual void SetValueSilently(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) = 0; + + protected: + ~WriteablePrefStore() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(WriteablePrefStore); +}; + +#endif // COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_
diff --git a/src/updater_components/update_client/BUILD.gn b/src/updater_components/update_client/BUILD.gn new file mode 100644 index 0000000..de3d06b --- /dev/null +++ b/src/updater_components/update_client/BUILD.gn
@@ -0,0 +1,243 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//net/features.gni") + +source_set("network_impl") { + sources = [ + "net/network_chromium.h", + "net/network_impl.cc", + "net/network_impl.h", + ] + + deps = [ + ":update_client", + "//base", + "//net", + "//services/network/public/cpp:cpp", + "//url", + ] +} + +source_set("unzip_impl") { + sources = [ + "unzip/unzip_impl.cc", + "unzip/unzip_impl.h", + ] + deps = [ + ":update_client", + "//components/services/unzip/public/cpp", + ] +} + +source_set("patch_impl") { + sources = [ + "patch/patch_impl.cc", + "patch/patch_impl.h", + ] + deps = [ + ":update_client", + "//components/services/patch/public/cpp", + "//components/services/patch/public/mojom", + "//mojo/public/cpp/bindings", + ] +} + +group("common_impl") { + public_deps = [ + ":network_impl", + ":patch_impl", + ":unzip_impl", + ] +} + +static_library("update_client") { + sources = [ + "action_runner.cc", + "action_runner.h", + "action_runner_win.cc", + "activity_data_service.h", + "background_downloader_win.cc", + "background_downloader_win.h", + "command_line_config_policy.cc", + "command_line_config_policy.h", + "component.cc", + "component.h", + "component_patcher.cc", + "component_patcher.h", + "component_patcher_operation.cc", + "component_patcher_operation.h", + "component_unpacker.cc", + "component_unpacker.h", + "configurator.h", + "crx_downloader.cc", + "crx_downloader.h", + "crx_update_item.h", + "network.cc", + "network.h", + "patcher.h", + "persisted_data.cc", + "persisted_data.h", + "ping_manager.cc", + "ping_manager.h", + "protocol_definition.cc", + "protocol_definition.h", + "protocol_handler.cc", + "protocol_handler.h", + "protocol_parser.cc", + "protocol_parser.h", + "protocol_parser_json.cc", + "protocol_parser_json.h", + "protocol_serializer.cc", + "protocol_serializer.h", + "protocol_serializer_json.cc", + "protocol_serializer_json.h", + "request_sender.cc", + "request_sender.h", + "task.h", + "task_send_uninstall_ping.cc", + "task_send_uninstall_ping.h", + "task_traits.h", + "task_update.cc", + "task_update.h", + "unzipper.h", + "update_checker.cc", + "update_checker.h", + "update_client.cc", + "update_client.h", + "update_client_errors.h", + "update_client_internal.h", + "update_engine.cc", + "update_engine.h", + "update_query_params.cc", + "update_query_params.h", + "update_query_params_delegate.cc", + "update_query_params_delegate.h", + "updater_state.cc", + "updater_state.h", + "updater_state_mac.mm", + "updater_state_win.cc", + "url_fetcher_downloader.cc", + "url_fetcher_downloader.h", + "utils.cc", + "utils.h", + ] + + deps = [ + "//base", + "//build:branding_buildflags", + "//components/client_update_protocol", + "//components/crx_file", + "//components/prefs", + "//components/version_info:version_info", + "//courgette:courgette_lib", + "//crypto", + "//url", + ] +} + +static_library("test_support") { + testonly = true + sources = [ + "net/url_loader_post_interceptor.cc", + "net/url_loader_post_interceptor.h", + "test_configurator.cc", + "test_configurator.h", + "test_installer.cc", + "test_installer.h", + ] + + public_deps = [ + ":update_client", + ] + + deps = [ + ":network_impl", + ":patch_impl", + ":unzip_impl", + "//base", + "//components/prefs", + "//components/services/patch:in_process", + "//components/services/unzip:in_process", + "//mojo/public/cpp/bindings", + "//net:test_support", + "//services/network:test_support", + "//testing/gmock", + "//testing/gtest", + "//url", + ] +} + +bundle_data("unit_tests_bundle_data") { + visibility = [ ":unit_tests" ] + testonly = true + sources = [ + "//components/test/data/update_client/binary_bsdiff_patch.bin", + "//components/test/data/update_client/binary_courgette_patch.bin", + "//components/test/data/update_client/binary_input.bin", + "//components/test/data/update_client/binary_output.bin", + "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx", + "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx", + "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx", + "//components/test/data/update_client/jebgalgnebhfojomionfpkfelancnnkf.crx", + "//components/test/data/update_client/runaction_test_win.crx3", + "//components/test/data/update_client/updatecheck_reply_1.json", + "//components/test/data/update_client/updatecheck_reply_4.json", + "//components/test/data/update_client/updatecheck_reply_noupdate.json", + "//components/test/data/update_client/updatecheck_reply_parse_error.json", + "//components/test/data/update_client/updatecheck_reply_unknownapp.json", + ] + outputs = [ + "{{bundle_resources_dir}}/" + + "{{source_root_relative_dir}}/{{source_file_part}}", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "component_patcher_unittest.cc", + "component_patcher_unittest.h", + "component_unpacker_unittest.cc", + "persisted_data_unittest.cc", + "ping_manager_unittest.cc", + "protocol_parser_json_unittest.cc", + "protocol_serializer_json_unittest.cc", + "protocol_serializer_unittest.cc", + "request_sender_unittest.cc", + "update_checker_unittest.cc", + "update_client_unittest.cc", + "update_query_params_unittest.cc", + "updater_state_unittest.cc", + "utils_unittest.cc", + ] + + if (!disable_file_support) { + sources += [ "crx_downloader_unittest.cc" ] + } + + deps = [ + ":network_impl", + ":patch_impl", + ":test_support", + ":unit_tests_bundle_data", + ":unzip_impl", + ":update_client", + "//base", + "//build:branding_buildflags", + "//components/crx_file", + "//components/prefs", + "//components/prefs:test_support", + "//components/services/patch:in_process", + "//components/version_info:version_info", + "//courgette:courgette_lib", + "//net:test_support", + "//services/network:test_support", + "//services/network/public/cpp:cpp", + "//services/network/public/cpp:cpp_base", + "//testing/gmock", + "//testing/gtest", + "//third_party/re2", + ] +}
diff --git a/src/updater_components/update_client/action_runner.cc b/src/updater_components/update_client/action_runner.cc new file mode 100644 index 0000000..e738bdc --- /dev/null +++ b/src/updater_components/update_client/action_runner.cc
@@ -0,0 +1,108 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/action_runner.h" + +#include <iterator> +#include <utility> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/task/post_task.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "components/crx_file/crx_verifier.h" +#include "components/update_client/component.h" +#include "components/update_client/configurator.h" +#include "components/update_client/patcher.h" +#include "components/update_client/task_traits.h" +#include "components/update_client/unzipper.h" +#include "components/update_client/update_client.h" +#include "components/update_client/update_engine.h" + +namespace update_client { + +ActionRunner::ActionRunner(const Component& component) + : is_per_user_install_(component.config()->IsPerUserInstall()), + component_(component), + main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {} + +ActionRunner::~ActionRunner() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +} + +void ActionRunner::Run(Callback run_complete) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + run_complete_ = std::move(run_complete); + + base::CreateSequencedTaskRunner(kTaskTraits) + ->PostTask( + FROM_HERE, + base::BindOnce(&ActionRunner::RunOnTaskRunner, base::Unretained(this), + component_.config()->GetUnzipperFactory()->Create(), + component_.config()->GetPatcherFactory()->Create())); +} + +void ActionRunner::RunOnTaskRunner(std::unique_ptr<Unzipper> unzip, + scoped_refptr<Patcher> patch) { + const auto installer = component_.crx_component()->installer; + + base::FilePath crx_path; + installer->GetInstalledFile(component_.action_run(), &crx_path); + + if (!is_per_user_install_) { + RunRecoveryCRXElevated(std::move(crx_path)); + return; + } + + const auto config = component_.config(); + auto unpacker = base::MakeRefCounted<ComponentUnpacker>( + config->GetRunActionKeyHash(), crx_path, installer, std::move(unzip), + std::move(patch), component_.crx_component()->crx_format_requirement); + unpacker->Unpack( + base::BindOnce(&ActionRunner::UnpackComplete, base::Unretained(this))); +} + +void ActionRunner::UnpackComplete(const ComponentUnpacker::Result& result) { + if (result.error != UnpackerError::kNone) { + DCHECK(!base::DirectoryExists(result.unpack_path)); + + main_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(std::move(run_complete_), false, + static_cast<int>(result.error), result.extended_error)); + return; + } + + unpack_path_ = result.unpack_path; + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&ActionRunner::RunCommand, base::Unretained(this), + MakeCommandLine(result.unpack_path))); +} + +#if !defined(OS_WIN) + +void ActionRunner::RunRecoveryCRXElevated(const base::FilePath& crx_path) { + NOTREACHED(); +} + +void ActionRunner::RunCommand(const base::CommandLine& cmdline) { + base::DeleteFile(unpack_path_, true); + main_task_runner_->PostTask( + FROM_HERE, base::BindOnce(std::move(run_complete_), false, -1, 0)); +} + +base::CommandLine ActionRunner::MakeCommandLine( + const base::FilePath& unpack_path) const { + return base::CommandLine(base::CommandLine::NO_PROGRAM); +} + +#endif // OS_WIN + +} // namespace update_client
diff --git a/src/updater_components/update_client/action_runner.h b/src/updater_components/update_client/action_runner.h new file mode 100644 index 0000000..ee41890 --- /dev/null +++ b/src/updater_components/update_client/action_runner.h
@@ -0,0 +1,75 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_ACTION_RUNNER_H_ +#define COMPONENTS_UPDATE_CLIENT_ACTION_RUNNER_H_ + +#include <stdint.h> + +#include <memory> +#include <utility> +#include <vector> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "build/build_config.h" +#include "components/update_client/component_unpacker.h" + +namespace base { +class CommandLine; +class Process; +class SingleThreadTaskRunner; +} + +namespace update_client { + +class Component; + +class ActionRunner { + public: + using Callback = + base::OnceCallback<void(bool succeeded, int error_code, int extra_code1)>; + + explicit ActionRunner(const Component& component); + ~ActionRunner(); + + void Run(Callback run_complete); + + private: + void RunOnTaskRunner(std::unique_ptr<Unzipper> unzipper, + scoped_refptr<Patcher> patcher); + void UnpackComplete(const ComponentUnpacker::Result& result); + + void RunCommand(const base::CommandLine& cmdline); + void RunRecoveryCRXElevated(const base::FilePath& crx_path); + + base::CommandLine MakeCommandLine(const base::FilePath& unpack_path) const; + + void WaitForCommand(base::Process process); + +#if defined(OS_WIN) + void RunRecoveryCRXElevatedInSTA(const base::FilePath& crx_path); +#endif + + bool is_per_user_install_ = false; + const Component& component_; + + // Used to post callbacks to the main thread. + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; + + // Contains the unpack path for the component associated with the run action. + base::FilePath unpack_path_; + + Callback run_complete_; + + THREAD_CHECKER(thread_checker_); + DISALLOW_COPY_AND_ASSIGN(ActionRunner); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_ACTION_RUNNER_H_
diff --git a/src/updater_components/update_client/action_runner_win.cc b/src/updater_components/update_client/action_runner_win.cc new file mode 100644 index 0000000..dca3059 --- /dev/null +++ b/src/updater_components/update_client/action_runner_win.cc
@@ -0,0 +1,90 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/action_runner.h" + +#include <tuple> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/location.h" +#include "base/process/launch.h" +#include "base/process/process.h" +#include "base/single_thread_task_runner.h" +#include "base/task/post_task.h" +#include "components/update_client/component.h" +#include "components/update_client/configurator.h" +#include "components/update_client/task_traits.h" + +namespace { + +const base::FilePath::CharType kRecoveryFileName[] = + FILE_PATH_LITERAL("ChromeRecovery.exe"); + +} // namespace + +namespace update_client { + +void ActionRunner::RunCommand(const base::CommandLine& cmdline) { + base::LaunchOptions options; + options.start_hidden = true; + base::Process process = base::LaunchProcess(cmdline, options); + + base::PostTask(FROM_HERE, kTaskTraitsRunCommand, + base::BindOnce(&ActionRunner::WaitForCommand, + base::Unretained(this), std::move(process))); +} + +void ActionRunner::WaitForCommand(base::Process process) { + int exit_code = 0; + const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromSeconds(600); + const bool succeeded = + process.WaitForExitWithTimeout(kMaxWaitTime, &exit_code); + base::DeleteFile(unpack_path_, true); + main_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(std::move(run_complete_), succeeded, exit_code, 0)); +} + +base::CommandLine ActionRunner::MakeCommandLine( + const base::FilePath& unpack_path) const { + base::CommandLine command_line(unpack_path.Append(kRecoveryFileName)); + if (!is_per_user_install_) + command_line.AppendSwitch("system"); + command_line.AppendSwitchASCII( + "browser-version", component_.config()->GetBrowserVersion().GetString()); + command_line.AppendSwitchASCII("sessionid", component_.session_id()); + const auto app_guid = component_.config()->GetAppGuid(); + if (!app_guid.empty()) + command_line.AppendSwitchASCII("appguid", app_guid); + VLOG(1) << "run action: " << command_line.GetCommandLineString(); + return command_line; +} + +void ActionRunner::RunRecoveryCRXElevated(const base::FilePath& crx_path) { + base::CreateCOMSTATaskRunner( + kTaskTraitsRunCommand, base::SingleThreadTaskRunnerThreadMode::DEDICATED) + ->PostTask(FROM_HERE, + base::BindOnce(&ActionRunner::RunRecoveryCRXElevatedInSTA, + base::Unretained(this), crx_path)); +} + +void ActionRunner::RunRecoveryCRXElevatedInSTA(const base::FilePath& crx_path) { + bool succeeded = false; + int error_code = 0; + int extra_code = 0; + const auto config = component_.config(); + std::tie(succeeded, error_code, extra_code) = + component_.config()->GetRecoveryCRXElevator().Run( + crx_path, config->GetAppGuid(), + config->GetBrowserVersion().GetString(), component_.session_id()); + main_task_runner_->PostTask( + FROM_HERE, base::BindOnce(std::move(run_complete_), succeeded, error_code, + extra_code)); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/activity_data_service.h b/src/updater_components/update_client/activity_data_service.h new file mode 100644 index 0000000..5d3744c --- /dev/null +++ b/src/updater_components/update_client/activity_data_service.h
@@ -0,0 +1,42 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_ACTIVITY_DATA_SERVICE_H_ +#define COMPONENTS_UPDATE_CLIENT_ACTIVITY_DATA_SERVICE_H_ + +#include <memory> +#include <string> + +#include "base/macros.h" + +namespace update_client { + +const int kDateFirstTime = -1; +const int kDaysFirstTime = -1; +const int kDateUnknown = -2; +const int kDaysUnknown = -2; + +// This is an interface that injects certain update information (active, days +// since ...) into the update engine of the update client. +// GetDaysSinceLastActive and GetDaysSinceLastRollCall are used for backward +// compatibility. +class ActivityDataService { + public: + // Returns the current state of the active bit of the specified |id|. + virtual bool GetActiveBit(const std::string& id) const = 0; + + // Clears the active bit of the specified |id|. + virtual void ClearActiveBit(const std::string& id) = 0; + + // The following 2 functions return the number of days since last + // active/roll call. + virtual int GetDaysSinceLastActive(const std::string& id) const = 0; + virtual int GetDaysSinceLastRollCall(const std::string& id) const = 0; + + virtual ~ActivityDataService() {} +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_ACTIVITY_DATA_SERVICE_H_
diff --git a/src/updater_components/update_client/background_downloader_win.cc b/src/updater_components/update_client/background_downloader_win.cc new file mode 100644 index 0000000..5e16579 --- /dev/null +++ b/src/updater_components/update_client/background_downloader_win.cc
@@ -0,0 +1,903 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/background_downloader_win.h" + +#include <objbase.h> +#include <winerror.h> + +#include <stddef.h> +#include <stdint.h> +#include <functional> +#include <iomanip> +#include <limits> +#include <memory> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/location.h" +#include "base/macros.h" +#include "base/metrics/histogram_macros.h" +#include "base/sequenced_task_runner.h" +#include "base/strings/string_piece.h" +#include "base/strings/sys_string_conversions.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#include "base/win/atl.h" +#include "base/win/scoped_co_mem.h" +#include "components/update_client/task_traits.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/utils.h" +#include "url/gurl.h" + +using Microsoft::WRL::ComPtr; +using base::win::ScopedCoMem; + +// The class BackgroundDownloader in this module is an adapter between +// the CrxDownloader interface and the BITS service interfaces. +// The interface exposed on the CrxDownloader code runs on the main thread, +// while the BITS specific code runs on a separate thread passed in by the +// client. For every url to download, a BITS job is created, unless there is +// already an existing job for that url, in which case, the downloader +// connects to it. Once a job is associated with the url, the code looks for +// changes in the BITS job state. The checks are triggered by a timer. +// The BITS job contains just one file to download. There could only be one +// download in progress at a time. If Chrome closes down before the download is +// complete, the BITS job remains active and finishes in the background, without +// any intervention. The job can be completed next time the code runs, if the +// file is still needed, otherwise it will be cleaned up on a periodic basis. +// +// To list the BITS jobs for a user, use the |bitsadmin| tool. The command line +// to do that is: "bitsadmin /list /verbose". Another useful command is +// "bitsadmin /info" and provide the job id returned by the previous /list +// command. +// +// Ignoring the suspend/resume issues since this code is not using them, the +// job state machine implemented by BITS is something like this: +// +// Suspended--->Queued--->Connecting---->Transferring--->Transferred +// | ^ | | | +// | | V V | (complete) +// +----------|---------+-----------------+-----+ V +// | | | | Acknowledged +// | V V | +// | Transient Error------->Error | +// | | | |(cancel) +// | +-------+---------+--->-+ +// | V | +// | (resume) | | +// +------<----------+ +---->Cancelled +// +// The job is created in the "suspended" state. Once |Resume| is called, +// BITS queues up the job, then tries to connect, begins transferring the +// job bytes, and moves the job to the "transferred state, after the job files +// have been transferred. When calling |Complete| for a job, the job files are +// made available to the caller, and the job is moved to the "acknowledged" +// state. +// At any point, the job can be cancelled, in which case, the job is moved +// to the "cancelled state" and the job object is removed from the BITS queue. +// Along the way, the job can encounter recoverable and non-recoverable errors. +// BITS moves the job to "transient error" or "error", depending on which kind +// of error has occured. +// If the job has reached the "transient error" state, BITS retries the +// job after a certain programmable delay. If the job can't be completed in a +// certain time interval, BITS stops retrying and errors the job out. This time +// interval is also programmable. +// If the job is in either of the error states, the job parameters can be +// adjusted to handle the error, after which the job can be resumed, and the +// whole cycle starts again. +// Jobs that are not touched in 90 days (or a value set by group policy) are +// automatically disposed off by BITS. This concludes the brief description of +// a job lifetime, according to BITS. +// +// In addition to how BITS is managing the life time of the job, there are a +// couple of special cases defined by the BackgroundDownloader. +// First, if the job encounters any of the 5xx HTTP responses, the job is +// not retried, in order to avoid DDOS-ing the servers. +// Second, there is a simple mechanism to detect stuck jobs, and allow the rest +// of the code to move on to trying other urls or trying other components. +// Last, after completing a job, irrespective of the outcome, the jobs older +// than a week are proactively cleaned up. + +namespace update_client { + +namespace { + +// All jobs created by this module have a specific description so they can +// be found at run-time or by using system administration tools. +const base::char16 kJobName[] = L"Chrome Component Updater"; + +// How often the code looks for changes in the BITS job state. +const int kJobPollingIntervalSec = 4; + +// How long BITS waits before retrying a job after the job encountered +// a transient error. If this value is not set, the BITS default is 10 minutes. +const int kMinimumRetryDelayMin = 1; + +// How long to wait for stuck jobs. Stuck jobs could be queued for too long, +// have trouble connecting, could be suspended for any reason, or they have +// encountered some transient error. +const int kJobStuckTimeoutMin = 15; + +// How long BITS waits before giving up on a job that could not be completed +// since the job has encountered its first transient error. If this value is +// not set, the BITS default is 14 days. +const int kSetNoProgressTimeoutDays = 1; + +// How often the jobs which were started but not completed for any reason +// are cleaned up. Reasons for jobs to be left behind include browser restarts, +// system restarts, etc. Also, the check to purge stale jobs only happens +// at most once a day. If the job clean up code is not running, the BITS +// default policy is to cancel jobs after 90 days of inactivity. +const int kPurgeStaleJobsAfterDays = 3; +const int kPurgeStaleJobsIntervalBetweenChecksDays = 1; + +// Number of maximum BITS jobs this downloader can create and queue up. +const int kMaxQueuedJobs = 10; + +// Retrieves the singleton instance of GIT for this process. +HRESULT GetGit(ComPtr<IGlobalInterfaceTable>* git) { + return ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, nullptr, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(git->GetAddressOf())); +} + +// Retrieves an interface pointer from the process GIT for a given |cookie|. +HRESULT GetInterfaceFromGit(const ComPtr<IGlobalInterfaceTable>& git, + DWORD cookie, + REFIID riid, + void** ppv) { + return git->GetInterfaceFromGlobal(cookie, riid, ppv); +} + +// Registers an interface pointer in GIT and returns its corresponding |cookie|. +template <typename T> +HRESULT RegisterInterfaceInGit(const ComPtr<IGlobalInterfaceTable>& git, + const ComPtr<T>& p, + DWORD* cookie) { + return git->RegisterInterfaceInGlobal(p.Get(), __uuidof(T), cookie); +} + +// Returns the status code from a given BITS error. +int GetHttpStatusFromBitsError(HRESULT error) { + // BITS errors are defined in bitsmsg.h. Although not documented, it is + // clear that all errors corresponding to http status code have the high + // word equal to 0x8019 and the low word equal to the http status code. + const int kHttpStatusFirst = 100; // Continue. + const int kHttpStatusLast = 505; // Version not supported. + bool is_valid = HIWORD(error) == 0x8019 && + LOWORD(error) >= kHttpStatusFirst && + LOWORD(error) <= kHttpStatusLast; + return is_valid ? LOWORD(error) : 0; +} + +// Returns the files in a BITS job. +HRESULT GetFilesInJob(const ComPtr<IBackgroundCopyJob>& job, + std::vector<ComPtr<IBackgroundCopyFile>>* files) { + ComPtr<IEnumBackgroundCopyFiles> enum_files; + HRESULT hr = job->EnumFiles(enum_files.GetAddressOf()); + if (FAILED(hr)) + return hr; + + ULONG num_files = 0; + hr = enum_files->GetCount(&num_files); + if (FAILED(hr)) + return hr; + + for (ULONG i = 0; i != num_files; ++i) { + ComPtr<IBackgroundCopyFile> file; + if (enum_files->Next(1, file.GetAddressOf(), nullptr) == S_OK && file.Get()) + files->push_back(file); + } + + return S_OK; +} + +// Returns the file name, the url, and some per-file progress information. +// The function out parameters can be NULL if that data is not requested. +HRESULT GetJobFileProperties(const ComPtr<IBackgroundCopyFile>& file, + base::string16* local_name, + base::string16* remote_name, + BG_FILE_PROGRESS* progress) { + if (!file) + return E_FAIL; + + HRESULT hr = S_OK; + + if (local_name) { + ScopedCoMem<base::char16> name; + hr = file->GetLocalName(&name); + if (FAILED(hr)) + return hr; + local_name->assign(name); + } + + if (remote_name) { + ScopedCoMem<base::char16> name; + hr = file->GetRemoteName(&name); + if (FAILED(hr)) + return hr; + remote_name->assign(name); + } + + if (progress) { + BG_FILE_PROGRESS bg_file_progress = {}; + hr = file->GetProgress(&bg_file_progress); + if (FAILED(hr)) + return hr; + *progress = bg_file_progress; + } + + return hr; +} + +// Returns the number of bytes downloaded and bytes to download for all files +// in the job. If the values are not known or if an error has occurred, +// a value of -1 is reported. +HRESULT GetJobByteCount(const ComPtr<IBackgroundCopyJob>& job, + int64_t* downloaded_bytes, + int64_t* total_bytes) { + *downloaded_bytes = -1; + *total_bytes = -1; + + if (!job) + return E_FAIL; + + BG_JOB_PROGRESS job_progress = {}; + HRESULT hr = job->GetProgress(&job_progress); + if (FAILED(hr)) + return hr; + + const uint64_t kMaxNumBytes = + static_cast<uint64_t>(std::numeric_limits<int64_t>::max()); + if (job_progress.BytesTransferred <= kMaxNumBytes) + *downloaded_bytes = job_progress.BytesTransferred; + + if (job_progress.BytesTotal <= kMaxNumBytes && + job_progress.BytesTotal != BG_SIZE_UNKNOWN) + *total_bytes = job_progress.BytesTotal; + + return S_OK; +} + +HRESULT GetJobDisplayName(const ComPtr<IBackgroundCopyJob>& job, + base::string16* name) { + ScopedCoMem<base::char16> local_name; + const HRESULT hr = job->GetDisplayName(&local_name); + if (FAILED(hr)) + return hr; + *name = local_name.get(); + return S_OK; +} + +// Returns the job error code in |error_code| if the job is in the transient +// or the final error state. Otherwise, the job error is not available and +// the function fails. +HRESULT GetJobError(const ComPtr<IBackgroundCopyJob>& job, + HRESULT* error_code_out) { + *error_code_out = S_OK; + ComPtr<IBackgroundCopyError> copy_error; + HRESULT hr = job->GetError(copy_error.GetAddressOf()); + if (FAILED(hr)) + return hr; + + BG_ERROR_CONTEXT error_context = BG_ERROR_CONTEXT_NONE; + HRESULT error_code = S_OK; + hr = copy_error->GetError(&error_context, &error_code); + if (FAILED(hr)) + return hr; + + *error_code_out = FAILED(error_code) ? error_code : E_FAIL; + return S_OK; +} + +// Finds the component updater jobs matching the given predicate. +// Returns S_OK if the function has found at least one job, returns S_FALSE if +// no job was found, and it returns an error otherwise. +template <class Predicate> +HRESULT FindBitsJobIf(Predicate pred, + const ComPtr<IBackgroundCopyManager>& bits_manager, + std::vector<ComPtr<IBackgroundCopyJob>>* jobs) { + ComPtr<IEnumBackgroundCopyJobs> enum_jobs; + HRESULT hr = bits_manager->EnumJobs(0, enum_jobs.GetAddressOf()); + if (FAILED(hr)) + return hr; + + ULONG job_count = 0; + hr = enum_jobs->GetCount(&job_count); + if (FAILED(hr)) + return hr; + + // Iterate over jobs, run the predicate, and select the job only if + // the job description matches the component updater jobs. + for (ULONG i = 0; i != job_count; ++i) { + ComPtr<IBackgroundCopyJob> current_job; + if (enum_jobs->Next(1, current_job.GetAddressOf(), nullptr) == S_OK && + pred(current_job)) { + base::string16 job_name; + hr = GetJobDisplayName(current_job, &job_name); + if (job_name.compare(kJobName) == 0) + jobs->push_back(current_job); + } + } + + return jobs->empty() ? S_FALSE : S_OK; +} + +bool JobCreationOlderThanDaysPredicate(ComPtr<IBackgroundCopyJob> job, + int num_days) { + BG_JOB_TIMES times = {}; + HRESULT hr = job->GetTimes(×); + if (FAILED(hr)) + return false; + + const base::TimeDelta time_delta(base::TimeDelta::FromDays(num_days)); + const base::Time creation_time(base::Time::FromFileTime(times.CreationTime)); + + return creation_time + time_delta < base::Time::Now(); +} + +bool JobFileUrlEqualPredicate(ComPtr<IBackgroundCopyJob> job, const GURL& url) { + std::vector<ComPtr<IBackgroundCopyFile>> files; + HRESULT hr = GetFilesInJob(job, &files); + if (FAILED(hr)) + return false; + + for (size_t i = 0; i != files.size(); ++i) { + ScopedCoMem<base::char16> remote_name; + if (SUCCEEDED(files[i]->GetRemoteName(&remote_name)) && + url == GURL(base::StringPiece16(remote_name))) + return true; + } + + return false; +} + +// Creates an instance of the BITS manager. +HRESULT CreateBitsManager(ComPtr<IBackgroundCopyManager>* bits_manager) { + ComPtr<IBackgroundCopyManager> local_bits_manager; + HRESULT hr = + ::CoCreateInstance(__uuidof(BackgroundCopyManager), nullptr, CLSCTX_ALL, + IID_PPV_ARGS(&local_bits_manager)); + if (FAILED(hr)) { + return hr; + } + *bits_manager = local_bits_manager; + return S_OK; +} + +void CleanupJob(const ComPtr<IBackgroundCopyJob>& job) { + if (!job) + return; + + // Get the file paths associated with this job before canceling the job. + // Canceling the job removes it from the BITS queue right away. It appears + // that it is still possible to query for the properties of the job after + // the job has been canceled. It seems safer though to get the paths first. + std::vector<ComPtr<IBackgroundCopyFile>> files; + GetFilesInJob(job, &files); + + std::vector<base::FilePath> paths; + for (const auto& file : files) { + base::string16 local_name; + HRESULT hr = GetJobFileProperties(file, &local_name, nullptr, nullptr); + if (SUCCEEDED(hr)) + paths.push_back(base::FilePath(local_name)); + } + + job->Cancel(); + + for (const auto& path : paths) + DeleteFileAndEmptyParentDirectory(path); +} + +} // namespace + +BackgroundDownloader::BackgroundDownloader( + std::unique_ptr<CrxDownloader> successor) + : CrxDownloader(std::move(successor)), + com_task_runner_( + base::CreateCOMSTATaskRunner(kTaskTraitsBackgroundDownloader)), + git_cookie_bits_manager_(0), + git_cookie_job_(0) {} + +BackgroundDownloader::~BackgroundDownloader() { + DCHECK(thread_checker_.CalledOnValidThread()); + timer_.reset(); +} + +void BackgroundDownloader::StartTimer() { + timer_.reset(new base::OneShotTimer); + timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(kJobPollingIntervalSec), + this, &BackgroundDownloader::OnTimer); +} + +void BackgroundDownloader::OnTimer() { + DCHECK(thread_checker_.CalledOnValidThread()); + com_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloading, + base::Unretained(this))); +} + +void BackgroundDownloader::DoStartDownload(const GURL& url) { + DCHECK(thread_checker_.CalledOnValidThread()); + com_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&BackgroundDownloader::BeginDownload, + base::Unretained(this), url)); +} + +// Called one time when this class is asked to do a download. +void BackgroundDownloader::BeginDownload(const GURL& url) { + DCHECK(com_task_runner_->RunsTasksInCurrentSequence()); + + download_start_time_ = base::TimeTicks::Now(); + job_stuck_begin_time_ = download_start_time_; + + HRESULT hr = BeginDownloadHelper(url); + if (FAILED(hr)) { + EndDownload(hr); + return; + } + + VLOG(1) << "Starting BITS download for: " << url.spec(); + + ResetInterfacePointers(); + main_task_runner()->PostTask(FROM_HERE, + base::BindOnce(&BackgroundDownloader::StartTimer, + base::Unretained(this))); +} + +// Creates or opens an existing BITS job to download the |url|, and handles +// the marshalling of the interfaces in GIT. +HRESULT BackgroundDownloader::BeginDownloadHelper(const GURL& url) { + ComPtr<IGlobalInterfaceTable> git; + HRESULT hr = GetGit(&git); + if (FAILED(hr)) + return hr; + + hr = CreateBitsManager(&bits_manager_); + if (FAILED(hr)) + return hr; + + hr = QueueBitsJob(url, &job_); + if (FAILED(hr)) + return hr; + + hr = RegisterInterfaceInGit(git, bits_manager_, &git_cookie_bits_manager_); + if (FAILED(hr)) + return hr; + + hr = RegisterInterfaceInGit(git, job_, &git_cookie_job_); + if (FAILED(hr)) + return hr; + + return S_OK; +} + +// Called any time the timer fires. +void BackgroundDownloader::OnDownloading() { + DCHECK(com_task_runner_->RunsTasksInCurrentSequence()); + + HRESULT hr = UpdateInterfacePointers(); + if (FAILED(hr)) { + EndDownload(hr); + return; + } + + BG_JOB_STATE job_state = BG_JOB_STATE_ERROR; + hr = job_->GetState(&job_state); + if (FAILED(hr)) { + EndDownload(hr); + return; + } + + bool is_handled = false; + switch (job_state) { + case BG_JOB_STATE_TRANSFERRED: + is_handled = OnStateTransferred(); + break; + + case BG_JOB_STATE_ERROR: + is_handled = OnStateError(); + break; + + case BG_JOB_STATE_CANCELLED: + is_handled = OnStateCancelled(); + break; + + case BG_JOB_STATE_ACKNOWLEDGED: + is_handled = OnStateAcknowledged(); + break; + + case BG_JOB_STATE_QUEUED: + // Fall through. + case BG_JOB_STATE_CONNECTING: + // Fall through. + case BG_JOB_STATE_SUSPENDED: + is_handled = OnStateQueued(); + break; + + case BG_JOB_STATE_TRANSIENT_ERROR: + is_handled = OnStateTransientError(); + break; + + case BG_JOB_STATE_TRANSFERRING: + is_handled = OnStateTransferring(); + break; + + default: + break; + } + + if (is_handled) + return; + + ResetInterfacePointers(); + main_task_runner()->PostTask(FROM_HERE, + base::BindOnce(&BackgroundDownloader::StartTimer, + base::Unretained(this))); +} + +// Completes the BITS download, picks up the file path of the response, and +// notifies the CrxDownloader. The function should be called only once. +void BackgroundDownloader::EndDownload(HRESULT error) { + DCHECK(com_task_runner_->RunsTasksInCurrentSequence()); + + const base::TimeTicks download_end_time(base::TimeTicks::Now()); + const base::TimeDelta download_time = + download_end_time >= download_start_time_ + ? download_end_time - download_start_time_ + : base::TimeDelta(); + + int64_t downloaded_bytes = -1; + int64_t total_bytes = -1; + GetJobByteCount(job_, &downloaded_bytes, &total_bytes); + + if (FAILED(error)) + CleanupJob(job_); + + ClearGit(); + + // Consider the url handled if it has been successfully downloaded or a + // 5xx has been received. + const bool is_handled = + SUCCEEDED(error) || IsHttpServerError(GetHttpStatusFromBitsError(error)); + + const int error_to_report = SUCCEEDED(error) ? 0 : error; + + DCHECK(static_cast<bool>(error_to_report) == !base::PathExists(response_)); + + DownloadMetrics download_metrics; + download_metrics.url = url(); + download_metrics.downloader = DownloadMetrics::kBits; + download_metrics.error = error_to_report; + download_metrics.downloaded_bytes = downloaded_bytes; + download_metrics.total_bytes = total_bytes; + download_metrics.download_time_ms = download_time.InMilliseconds(); + + Result result; + result.error = error_to_report; + if (!result.error) + result.response = response_; + main_task_runner()->PostTask( + FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloadComplete, + base::Unretained(this), is_handled, result, + download_metrics)); + + // Once the task is posted to the the main thread, this object may be deleted + // by its owner. It is not safe to access members of this object on this task + // runner from now on. +} + +// Called when the BITS job has been transferred successfully. Completes the +// BITS job by removing it from the BITS queue and making the download +// available to the caller. +bool BackgroundDownloader::OnStateTransferred() { + EndDownload(CompleteJob()); + return true; +} + +// Called when the job has encountered an error and no further progress can +// be made. Cancels this job and removes it from the BITS queue. +bool BackgroundDownloader::OnStateError() { + HRESULT error_code = S_OK; + HRESULT hr = GetJobError(job_, &error_code); + if (FAILED(hr)) + error_code = hr; + + DCHECK(FAILED(error_code)); + EndDownload(error_code); + return true; +} + +// Called when the download was completed. This notification is not seen +// in the current implementation but provided here as a defensive programming +// measure. +bool BackgroundDownloader::OnStateAcknowledged() { + EndDownload(E_UNEXPECTED); + return true; +} + +// Called when the download was cancelled. Same as above. +bool BackgroundDownloader::OnStateCancelled() { + EndDownload(E_UNEXPECTED); + return true; +} + +// Called when the job has encountered a transient error, such as a +// network disconnect, a server error, or some other recoverable error. +bool BackgroundDownloader::OnStateTransientError() { + // If the job appears to be stuck, handle the transient error as if + // it were a final error. This causes the job to be cancelled and a specific + // error be returned, if the error was available. + if (IsStuck()) { + return OnStateError(); + } + + // Don't retry at all if the transient error was a 5xx. + HRESULT error_code = S_OK; + HRESULT hr = GetJobError(job_, &error_code); + if (SUCCEEDED(hr) && + IsHttpServerError(GetHttpStatusFromBitsError(error_code))) { + return OnStateError(); + } + + return false; +} + +bool BackgroundDownloader::OnStateQueued() { + if (!IsStuck()) + return false; + + // Terminate the download if the job has not made progress in a while. + EndDownload(E_ABORT); + return true; +} + +bool BackgroundDownloader::OnStateTransferring() { + // Resets the baseline for detecting a stuck job since the job is transferring + // data and it is making progress. + job_stuck_begin_time_ = base::TimeTicks::Now(); + + main_task_runner()->PostTask( + FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloadProgress, + base::Unretained(this))); + return false; +} + +HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url, + ComPtr<IBackgroundCopyJob>* job) { + DCHECK(com_task_runner_->RunsTasksInCurrentSequence()); + + size_t num_jobs = std::numeric_limits<size_t>::max(); + HRESULT hr = GetBackgroundDownloaderJobCount(&num_jobs); + + // The metric records a large number if the job count can't be read. + UMA_HISTOGRAM_COUNTS_100("UpdateClient.BackgroundDownloaderJobs", num_jobs); + + // Remove some old jobs from the BITS queue before creating new jobs. + CleanupStaleJobs(); + + if (FAILED(hr) || num_jobs >= kMaxQueuedJobs) + return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, + CrxDownloaderError::BITS_TOO_MANY_JOBS); + + ComPtr<IBackgroundCopyJob> local_job; + hr = CreateOrOpenJob(url, &local_job); + if (FAILED(hr)) { + CleanupJob(local_job); + return hr; + } + + const bool is_new_job = hr == S_OK; + UMA_HISTOGRAM_BOOLEAN("UpdateClient.BackgroundDownloaderNewJob", is_new_job); + + hr = local_job->Resume(); + if (FAILED(hr)) { + CleanupJob(local_job); + return hr; + } + + *job = local_job; + return S_OK; +} + +HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url, + ComPtr<IBackgroundCopyJob>* job) { + std::vector<ComPtr<IBackgroundCopyJob>> jobs; + HRESULT hr = FindBitsJobIf( + [&url](ComPtr<IBackgroundCopyJob> job) { + return JobFileUrlEqualPredicate(job, url); + }, + bits_manager_, &jobs); + if (SUCCEEDED(hr) && !jobs.empty()) { + *job = jobs.front(); + return S_FALSE; + } + + ComPtr<IBackgroundCopyJob> local_job; + + GUID guid = {0}; + hr = bits_manager_->CreateJob(kJobName, BG_JOB_TYPE_DOWNLOAD, &guid, + local_job.GetAddressOf()); + if (FAILED(hr)) { + CleanupJob(local_job); + return hr; + } + + hr = InitializeNewJob(local_job, url); + if (FAILED(hr)) { + CleanupJob(local_job); + return hr; + } + + *job = local_job; + return S_OK; +} + +HRESULT BackgroundDownloader::InitializeNewJob( + const ComPtr<IBackgroundCopyJob>& job, + const GURL& url) { + base::FilePath tempdir; + if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_BITS_"), + &tempdir)) + return E_FAIL; + + const base::string16 filename(base::SysUTF8ToWide(url.ExtractFileName())); + HRESULT hr = job->AddFile(base::SysUTF8ToWide(url.spec()).c_str(), + tempdir.Append(filename).AsUTF16Unsafe().c_str()); + if (FAILED(hr)) + return hr; + + hr = job->SetDescription(filename.c_str()); + if (FAILED(hr)) + return hr; + + hr = job->SetPriority(BG_JOB_PRIORITY_NORMAL); + if (FAILED(hr)) + return hr; + + hr = job->SetMinimumRetryDelay(60 * kMinimumRetryDelayMin); + if (FAILED(hr)) + return hr; + + const int kSecondsDay = 60 * 60 * 24; + hr = job->SetNoProgressTimeout(kSecondsDay * kSetNoProgressTimeoutDays); + if (FAILED(hr)) + return hr; + + return S_OK; +} + +bool BackgroundDownloader::IsStuck() { + const base::TimeDelta job_stuck_timeout( + base::TimeDelta::FromMinutes(kJobStuckTimeoutMin)); + return job_stuck_begin_time_ + job_stuck_timeout < base::TimeTicks::Now(); +} + +HRESULT BackgroundDownloader::CompleteJob() { + HRESULT hr = job_->Complete(); + if (FAILED(hr) && hr != BG_S_UNABLE_TO_DELETE_FILES) + return hr; + + std::vector<ComPtr<IBackgroundCopyFile>> files; + hr = GetFilesInJob(job_, &files); + if (FAILED(hr)) + return hr; + + if (files.empty()) + return E_UNEXPECTED; + + base::string16 local_name; + BG_FILE_PROGRESS progress = {0}; + hr = GetJobFileProperties(files.front(), &local_name, nullptr, &progress); + if (FAILED(hr)) + return hr; + + // Sanity check the post-conditions of a successful download, including + // the file and job invariants. The byte counts for a job and its file + // must match as a job only contains one file. + DCHECK(progress.Completed); + DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred); + + response_ = base::FilePath(local_name); + + return S_OK; +} + +HRESULT BackgroundDownloader::UpdateInterfacePointers() { + DCHECK(com_task_runner_->RunsTasksInCurrentSequence()); + + bits_manager_ = nullptr; + job_ = nullptr; + + ComPtr<IGlobalInterfaceTable> git; + HRESULT hr = GetGit(&git); + if (FAILED(hr)) + return hr; + + hr = GetInterfaceFromGit(git, git_cookie_bits_manager_, + IID_PPV_ARGS(bits_manager_.GetAddressOf())); + if (FAILED(hr)) + return hr; + + hr = GetInterfaceFromGit(git, git_cookie_job_, + IID_PPV_ARGS(job_.GetAddressOf())); + if (FAILED(hr)) + return hr; + + return S_OK; +} + +void BackgroundDownloader::ResetInterfacePointers() { + job_ = nullptr; + bits_manager_ = nullptr; +} + +HRESULT BackgroundDownloader::ClearGit() { + DCHECK(com_task_runner_->RunsTasksInCurrentSequence()); + + ResetInterfacePointers(); + + ComPtr<IGlobalInterfaceTable> git; + HRESULT hr = GetGit(&git); + if (FAILED(hr)) + return hr; + + const DWORD cookies[] = { + git_cookie_job_, git_cookie_bits_manager_ + }; + + for (auto cookie : cookies) { + // TODO(sorin): check the result of the call, see crbug.com/644857. + git->RevokeInterfaceFromGlobal(cookie); + } + + return S_OK; +} + +HRESULT BackgroundDownloader::GetBackgroundDownloaderJobCount( + size_t* num_jobs) { + DCHECK(com_task_runner_->RunsTasksInCurrentSequence()); + DCHECK(bits_manager_); + + std::vector<ComPtr<IBackgroundCopyJob>> jobs; + const HRESULT hr = + FindBitsJobIf([](const ComPtr<IBackgroundCopyJob>&) { return true; }, + bits_manager_, &jobs); + if (FAILED(hr)) + return hr; + + *num_jobs = jobs.size(); + return S_OK; +} + +void BackgroundDownloader::CleanupStaleJobs() { + DCHECK(com_task_runner_->RunsTasksInCurrentSequence()); + DCHECK(bits_manager_); + + static base::Time last_sweep; + + const base::TimeDelta time_delta( + base::TimeDelta::FromDays(kPurgeStaleJobsIntervalBetweenChecksDays)); + const base::Time current_time(base::Time::Now()); + if (last_sweep + time_delta > current_time) + return; + + last_sweep = current_time; + + std::vector<ComPtr<IBackgroundCopyJob>> jobs; + FindBitsJobIf( + [](ComPtr<IBackgroundCopyJob> job) { + return JobCreationOlderThanDaysPredicate(job, kPurgeStaleJobsAfterDays); + }, + bits_manager_, &jobs); + + for (const auto& job : jobs) + CleanupJob(job); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/background_downloader_win.h b/src/updater_components/update_client/background_downloader_win.h new file mode 100644 index 0000000..28b9693 --- /dev/null +++ b/src/updater_components/update_client/background_downloader_win.h
@@ -0,0 +1,157 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_BACKGROUND_DOWNLOADER_WIN_H_ +#define COMPONENTS_UPDATE_CLIENT_BACKGROUND_DOWNLOADER_WIN_H_ + +#include <bits.h> +#include <windows.h> +#include <wrl/client.h> + +#include <memory> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "base/timer/timer.h" +#include "components/update_client/crx_downloader.h" + +namespace base { +class FilePath; +class SequencedTaskRunner; +} + +namespace update_client { + +// Implements a downloader in terms of the BITS service. The public interface +// of this class and the CrxDownloader overrides are expected to be called +// from the main thread. The rest of the class code runs on a sequenced +// task runner, usually associated with a blocking thread pool. The task runner +// must initialize COM. +// +// This class manages a COM client for Windows BITS. The client uses polling, +// triggered by an one-shot timer, to get state updates from BITS. Since the +// timer has thread afinity, the callbacks from the timer are delegated to +// a sequenced task runner, which handles all client COM interaction with +// the BITS service. +class BackgroundDownloader : public CrxDownloader { + public: + explicit BackgroundDownloader(std::unique_ptr<CrxDownloader> successor); + ~BackgroundDownloader() override; + + private: + // Overrides for CrxDownloader. + void DoStartDownload(const GURL& url) override; + + // Called asynchronously on the |com_task_runner_| at different stages during + // the download. |OnDownloading| can be called multiple times. + // |EndDownload| switches the execution flow from the |com_task_runner_| to + // the main thread. Accessing any data members of this object from the + // |com_task_runner_| after calling |EndDownload| is unsafe. + void BeginDownload(const GURL& url); + void OnDownloading(); + void EndDownload(HRESULT hr); + + HRESULT BeginDownloadHelper(const GURL& url); + + // Handles the job state transitions to a final state. Returns true always + // since the download has reached a final state and no further processing for + // this download is needed. + bool OnStateTransferred(); + bool OnStateError(); + bool OnStateCancelled(); + bool OnStateAcknowledged(); + + // Handles the transition to a transient state where the job is in the + // queue but not actively transferring data. Returns true if the download has + // been in this state for too long and it will be abandoned, or false, if + // further processing for this download is needed. + bool OnStateQueued(); + + // Handles the job state transition to a transient error state, which may or + // may not be considered final, depending on the error. Returns true if + // the state is final, or false, if the download is allowed to continue. + bool OnStateTransientError(); + + // Handles the job state corresponding to transferring data. Returns false + // always since this is never a final state. + bool OnStateTransferring(); + + void StartTimer(); + void OnTimer(); + + // Creates or opens a job for the given url and queues it up. Returns S_OK if + // a new job was created or S_FALSE if an existing job for the |url| was found + // in the BITS queue. + HRESULT QueueBitsJob(const GURL& url, + Microsoft::WRL::ComPtr<IBackgroundCopyJob>* job); + HRESULT CreateOrOpenJob(const GURL& url, + Microsoft::WRL::ComPtr<IBackgroundCopyJob>* job); + HRESULT InitializeNewJob( + const Microsoft::WRL::ComPtr<IBackgroundCopyJob>& job, + const GURL& url); + + // Returns true if at the time of the call, it appears that the job + // has not been making progress toward completion. + bool IsStuck(); + + // Makes the downloaded file available to the caller by renaming the + // temporary file to its destination and removing it from the BITS queue. + HRESULT CompleteJob(); + + // Revokes the interface pointers from GIT. + HRESULT ClearGit(); + + // Updates the BITS interface pointers so that they can be used by the + // thread calling the function. Call this function to get valid COM interface + // pointers when a thread from the thread pool enters the object. + HRESULT UpdateInterfacePointers(); + + // Resets the BITS interface pointers. Call this function when a thread + // from the thread pool leaves the object to release the interface pointers. + void ResetInterfacePointers(); + + // Returns the number of jobs in the BITS queue which were created by this + // downloader. + HRESULT GetBackgroundDownloaderJobCount(size_t* num_jobs); + + // Cleans up incompleted jobs that are too old. + void CleanupStaleJobs(); + + // Ensures that we are running on the same thread we created the object on. + base::ThreadChecker thread_checker_; + + // Executes blocking COM calls to BITS. + scoped_refptr<base::SequencedTaskRunner> com_task_runner_; + + // The timer has thread affinity. This member is initialized and destroyed + // on the main task runner. + std::unique_ptr<base::OneShotTimer> timer_; + + DWORD git_cookie_bits_manager_; + DWORD git_cookie_job_; + + // COM interface pointers are valid for the thread that called + // |UpdateInterfacePointers| to get pointers to COM proxies, which are valid + // for that thread only. + Microsoft::WRL::ComPtr<IBackgroundCopyManager> bits_manager_; + Microsoft::WRL::ComPtr<IBackgroundCopyJob> job_; + + // Contains the time when the download of the current url has started. + base::TimeTicks download_start_time_; + + // Contains the time when the BITS job is last seen making progress. + base::TimeTicks job_stuck_begin_time_; + + // Contains the path of the downloaded file if the download was successful. + base::FilePath response_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundDownloader); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_BACKGROUND_DOWNLOADER_WIN_H_
diff --git a/src/updater_components/update_client/command_line_config_policy.cc b/src/updater_components/update_client/command_line_config_policy.cc new file mode 100644 index 0000000..899d8ac --- /dev/null +++ b/src/updater_components/update_client/command_line_config_policy.cc
@@ -0,0 +1,44 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/command_line_config_policy.h" + +#include "build/build_config.h" +#include "url/gurl.h" + +namespace update_client { + +bool CommandLineConfigPolicy::BackgroundDownloadsEnabled() const { +#if defined(OS_WIN) + return true; +#else + return false; +#endif +} + +bool CommandLineConfigPolicy::DeltaUpdatesEnabled() const { + return true; +} + +bool CommandLineConfigPolicy::FastUpdate() const { + return false; +} + +bool CommandLineConfigPolicy::PingsEnabled() const { + return true; +} + +bool CommandLineConfigPolicy::TestRequest() const { + return false; +} + +GURL CommandLineConfigPolicy::UrlSourceOverride() const { + return GURL(); +} + +int CommandLineConfigPolicy::InitialDelay() const { + return 0; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/command_line_config_policy.h b/src/updater_components/update_client/command_line_config_policy.h new file mode 100644 index 0000000..2d201a5 --- /dev/null +++ b/src/updater_components/update_client/command_line_config_policy.h
@@ -0,0 +1,44 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_COMMAND_LINE_CONFIG_POLICY_H_ +#define COMPONENTS_UPDATE_CLIENT_COMMAND_LINE_CONFIG_POLICY_H_ + +class GURL; + +namespace update_client { + +// This class provides additional settings from command line switches to the +// main configurator. +class CommandLineConfigPolicy { + public: + // If true, background downloads are enabled. + virtual bool BackgroundDownloadsEnabled() const; + + // If true, differential updates are enabled. + virtual bool DeltaUpdatesEnabled() const; + + // If true, speed up the initial update checking. + virtual bool FastUpdate() const; + + // If true, pings are enabled. Pings are the requests sent to the update + // server that report the success or failure of installs or update attempts. + virtual bool PingsEnabled() const; + + // If true, add "testrequest" attribute to update check requests. + virtual bool TestRequest() const; + + // The override URL for updates. Can be empty. + virtual GURL UrlSourceOverride() const; + + // If non-zero, time interval in seconds until the first component + // update check. + virtual int InitialDelay() const; + + virtual ~CommandLineConfigPolicy() {} +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_COMMAND_LINE_CONFIG_POLICY_H_
diff --git a/src/updater_components/update_client/component.cc b/src/updater_components/update_client/component.cc new file mode 100644 index 0000000..7f5ecb5 --- /dev/null +++ b/src/updater_components/update_client/component.cc
@@ -0,0 +1,946 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/component.h" + +#include <algorithm> +#include <utility> + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/task/post_task.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/values.h" +#include "components/update_client/action_runner.h" +#include "components/update_client/component_unpacker.h" +#include "components/update_client/configurator.h" +#include "components/update_client/network.h" +#include "components/update_client/patcher.h" +#include "components/update_client/protocol_definition.h" +#include "components/update_client/protocol_serializer.h" +#include "components/update_client/task_traits.h" +#include "components/update_client/unzipper.h" +#include "components/update_client/update_client.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/update_engine.h" +#include "components/update_client/utils.h" + +// The state machine representing how a CRX component changes during an update. +// +// +------------------------- kNew +// | | +// | V +// | kChecking +// | | +// V error V no no +// kUpdateError <------------- [update?] -> [action?] -> kUpToDate kUpdated +// ^ | | ^ ^ +// | yes | | yes | | +// | V | | | +// | kCanUpdate +--------> kRun | +// | | | +// | no V | +// | +-<- [differential update?] | +// | | | | +// | | yes | | +// | | error V | +// | +-<----- kDownloadingDiff kRun---->-+ +// | | | ^ | +// | | | yes | | +// | | error V | | +// | +-<----- kUpdatingDiff ---------> [action?] ->-+ +// | | ^ no +// | error V | +// +-<-------- kDownloading | +// | | | +// | | | +// | error V | +// +-<-------- kUpdating --------------------------------+ + +namespace update_client { + +namespace { + +using InstallOnBlockingTaskRunnerCompleteCallback = base::OnceCallback< + void(ErrorCategory error_category, int error_code, int extra_code1)>; + +void InstallComplete( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + InstallOnBlockingTaskRunnerCompleteCallback callback, + const base::FilePath& unpack_path, + const CrxInstaller::Result& result) { + base::PostTask( + FROM_HERE, + {base::ThreadPool(), base::TaskPriority::BEST_EFFORT, base::MayBlock()}, + base::BindOnce( + [](scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + InstallOnBlockingTaskRunnerCompleteCallback callback, + const base::FilePath& unpack_path, + const CrxInstaller::Result& result) { + base::DeleteFile(unpack_path, true); + const ErrorCategory error_category = + result.error ? ErrorCategory::kInstall : ErrorCategory::kNone; + main_task_runner->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), error_category, + static_cast<int>(result.error), + result.extended_error)); + }, + main_task_runner, std::move(callback), unpack_path, result)); +} + +void InstallOnBlockingTaskRunner( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + const base::FilePath& unpack_path, + const std::string& public_key, + const std::string& fingerprint, + scoped_refptr<CrxInstaller> installer, + InstallOnBlockingTaskRunnerCompleteCallback callback) { + DCHECK(base::DirectoryExists(unpack_path)); + + // Acquire the ownership of the |unpack_path|. + base::ScopedTempDir unpack_path_owner; + ignore_result(unpack_path_owner.Set(unpack_path)); + + if (static_cast<int>(fingerprint.size()) != + base::WriteFile( + unpack_path.Append(FILE_PATH_LITERAL("manifest.fingerprint")), + fingerprint.c_str(), base::checked_cast<int>(fingerprint.size()))) { + const CrxInstaller::Result result(InstallError::FINGERPRINT_WRITE_FAILED); + main_task_runner->PostTask( + FROM_HERE, + base::BindOnce(std::move(callback), ErrorCategory::kInstall, + static_cast<int>(result.error), result.extended_error)); + return; + } + + installer->Install( + unpack_path, public_key, + base::BindOnce(&InstallComplete, main_task_runner, std::move(callback), + unpack_path_owner.Take())); +} + +void UnpackCompleteOnBlockingTaskRunner( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + const base::FilePath& crx_path, + const std::string& fingerprint, + scoped_refptr<CrxInstaller> installer, + InstallOnBlockingTaskRunnerCompleteCallback callback, + const ComponentUnpacker::Result& result) { + update_client::DeleteFileAndEmptyParentDirectory(crx_path); + + if (result.error != UnpackerError::kNone) { + main_task_runner->PostTask( + FROM_HERE, + base::BindOnce(std::move(callback), ErrorCategory::kUnpack, + static_cast<int>(result.error), result.extended_error)); + return; + } + + base::PostTask(FROM_HERE, kTaskTraits, + base::BindOnce(&InstallOnBlockingTaskRunner, main_task_runner, + result.unpack_path, result.public_key, + fingerprint, installer, std::move(callback))); +} + +void StartInstallOnBlockingTaskRunner( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + const std::vector<uint8_t>& pk_hash, + const base::FilePath& crx_path, + const std::string& fingerprint, + scoped_refptr<CrxInstaller> installer, + std::unique_ptr<Unzipper> unzipper_, + scoped_refptr<Patcher> patcher_, + crx_file::VerifierFormat crx_format, + InstallOnBlockingTaskRunnerCompleteCallback callback) { + auto unpacker = base::MakeRefCounted<ComponentUnpacker>( + pk_hash, crx_path, installer, std::move(unzipper_), std::move(patcher_), + crx_format); + + unpacker->Unpack(base::BindOnce(&UnpackCompleteOnBlockingTaskRunner, + main_task_runner, crx_path, fingerprint, + installer, std::move(callback))); +} + +// Returns a string literal corresponding to the value of the downloader |d|. +const char* DownloaderToString(CrxDownloader::DownloadMetrics::Downloader d) { + switch (d) { + case CrxDownloader::DownloadMetrics::kUrlFetcher: + return "direct"; + case CrxDownloader::DownloadMetrics::kBits: + return "bits"; + default: + return "unknown"; + } +} + +} // namespace + +Component::Component(const UpdateContext& update_context, const std::string& id) + : id_(id), + state_(std::make_unique<StateNew>(this)), + update_context_(update_context) {} + +Component::~Component() {} + +scoped_refptr<Configurator> Component::config() const { + return update_context_.config; +} + +std::string Component::session_id() const { + return update_context_.session_id; +} + +bool Component::is_foreground() const { + return update_context_.is_foreground; +} + +void Component::Handle(CallbackHandleComplete callback_handle_complete) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(state_); + + callback_handle_complete_ = std::move(callback_handle_complete); + + state_->Handle( + base::BindOnce(&Component::ChangeState, base::Unretained(this))); +} + +void Component::ChangeState(std::unique_ptr<State> next_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + + previous_state_ = state(); + if (next_state) + state_ = std::move(next_state); + else + is_handled_ = true; + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(callback_handle_complete_)); +} + +CrxUpdateItem Component::GetCrxUpdateItem() const { + DCHECK(thread_checker_.CalledOnValidThread()); + + CrxUpdateItem crx_update_item; + crx_update_item.state = state_->state(); + crx_update_item.id = id_; + if (crx_component_) + crx_update_item.component = *crx_component_; + crx_update_item.last_check = last_check_; + crx_update_item.next_version = next_version_; + crx_update_item.next_fp = next_fp_; + crx_update_item.error_category = error_category_; + crx_update_item.error_code = error_code_; + crx_update_item.extra_code1 = extra_code1_; + + return crx_update_item; +} + +void Component::SetParseResult(const ProtocolParser::Result& result) { + DCHECK(thread_checker_.CalledOnValidThread()); + + DCHECK_EQ(0, update_check_error_); + + status_ = result.status; + action_run_ = result.action_run; + + if (result.manifest.packages.empty()) + return; + + next_version_ = base::Version(result.manifest.version); + const auto& package = result.manifest.packages.front(); + next_fp_ = package.fingerprint; + + // Resolve the urls by combining the base urls with the package names. + for (const auto& crx_url : result.crx_urls) { + const GURL url = crx_url.Resolve(package.name); + if (url.is_valid()) + crx_urls_.push_back(url); + } + for (const auto& crx_diffurl : result.crx_diffurls) { + const GURL url = crx_diffurl.Resolve(package.namediff); + if (url.is_valid()) + crx_diffurls_.push_back(url); + } + + hash_sha256_ = package.hash_sha256; + hashdiff_sha256_ = package.hashdiff_sha256; +} + +void Component::Uninstall(const base::Version& version, int reason) { + DCHECK(thread_checker_.CalledOnValidThread()); + + DCHECK_EQ(ComponentState::kNew, state()); + + crx_component_ = CrxComponent(); + crx_component_->version = version; + + previous_version_ = version; + next_version_ = base::Version("0"); + extra_code1_ = reason; + + state_ = std::make_unique<StateUninstalled>(this); +} + +void Component::SetUpdateCheckResult( + const base::Optional<ProtocolParser::Result>& result, + ErrorCategory error_category, + int error) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(ComponentState::kChecking, state()); + + error_category_ = error_category; + error_code_ = error; + if (result) + SetParseResult(result.value()); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(update_check_complete_)); +} + +bool Component::CanDoBackgroundDownload() const { + // Foreground component updates are always downloaded in foreground. + return !is_foreground() && + (crx_component() && crx_component()->allows_background_download) && + update_context_.config->EnabledBackgroundDownloader(); +} + +void Component::AppendEvent(base::Value event) { + events_.push_back(std::move(event)); +} + +void Component::NotifyObservers(UpdateClient::Observer::Events event) const { + DCHECK(thread_checker_.CalledOnValidThread()); + update_context_.notify_observers_callback.Run(event, id_); +} + +base::TimeDelta Component::GetUpdateDuration() const { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (update_begin_.is_null()) + return base::TimeDelta(); + + const base::TimeDelta update_cost(base::TimeTicks::Now() - update_begin_); + DCHECK_GE(update_cost, base::TimeDelta()); + const base::TimeDelta max_update_delay = + base::TimeDelta::FromSeconds(update_context_.config->UpdateDelay()); + return std::min(update_cost, max_update_delay); +} + +base::Value Component::MakeEventUpdateComplete() const { + base::Value event(base::Value::Type::DICTIONARY); + event.SetKey("eventtype", base::Value(3)); + event.SetKey( + "eventresult", + base::Value(static_cast<int>(state() == ComponentState::kUpdated))); + if (error_category() != ErrorCategory::kNone) + event.SetKey("errorcat", base::Value(static_cast<int>(error_category()))); + if (error_code()) + event.SetKey("errorcode", base::Value(error_code())); + if (extra_code1()) + event.SetKey("extracode1", base::Value(extra_code1())); + if (HasDiffUpdate(*this)) { + const int diffresult = static_cast<int>(!diff_update_failed()); + event.SetKey("diffresult", base::Value(diffresult)); + } + if (diff_error_category() != ErrorCategory::kNone) { + const int differrorcat = static_cast<int>(diff_error_category()); + event.SetKey("differrorcat", base::Value(differrorcat)); + } + if (diff_error_code()) + event.SetKey("differrorcode", base::Value(diff_error_code())); + if (diff_extra_code1()) + event.SetKey("diffextracode1", base::Value(diff_extra_code1())); + if (!previous_fp().empty()) + event.SetKey("previousfp", base::Value(previous_fp())); + if (!next_fp().empty()) + event.SetKey("nextfp", base::Value(next_fp())); + DCHECK(previous_version().IsValid()); + event.SetKey("previousversion", base::Value(previous_version().GetString())); + if (next_version().IsValid()) + event.SetKey("nextversion", base::Value(next_version().GetString())); + return event; +} + +base::Value Component::MakeEventDownloadMetrics( + const CrxDownloader::DownloadMetrics& dm) const { + base::Value event(base::Value::Type::DICTIONARY); + event.SetKey("eventtype", base::Value(14)); + event.SetKey("eventresult", base::Value(static_cast<int>(dm.error == 0))); + event.SetKey("downloader", base::Value(DownloaderToString(dm.downloader))); + if (dm.error) + event.SetKey("errorcode", base::Value(dm.error)); + event.SetKey("url", base::Value(dm.url.spec())); + + // -1 means that the byte counts are not known. + if (dm.total_bytes != -1 && dm.total_bytes < kProtocolMaxInt) + event.SetKey("total", base::Value(static_cast<double>(dm.total_bytes))); + if (dm.downloaded_bytes != -1 && dm.total_bytes < kProtocolMaxInt) { + event.SetKey("downloaded", + base::Value(static_cast<double>(dm.downloaded_bytes))); + } + if (dm.download_time_ms && dm.total_bytes < kProtocolMaxInt) { + event.SetKey("download_time_ms", + base::Value(static_cast<double>(dm.download_time_ms))); + } + DCHECK(previous_version().IsValid()); + event.SetKey("previousversion", base::Value(previous_version().GetString())); + if (next_version().IsValid()) + event.SetKey("nextversion", base::Value(next_version().GetString())); + return event; +} + +base::Value Component::MakeEventUninstalled() const { + DCHECK(state() == ComponentState::kUninstalled); + base::Value event(base::Value::Type::DICTIONARY); + event.SetKey("eventtype", base::Value(4)); + event.SetKey("eventresult", base::Value(1)); + if (extra_code1()) + event.SetKey("extracode1", base::Value(extra_code1())); + DCHECK(previous_version().IsValid()); + event.SetKey("previousversion", base::Value(previous_version().GetString())); + DCHECK(next_version().IsValid()); + event.SetKey("nextversion", base::Value(next_version().GetString())); + return event; +} + +base::Value Component::MakeEventActionRun(bool succeeded, + int error_code, + int extra_code1) const { + base::Value event(base::Value::Type::DICTIONARY); + event.SetKey("eventtype", base::Value(42)); + event.SetKey("eventresult", base::Value(static_cast<int>(succeeded))); + if (error_code) + event.SetKey("errorcode", base::Value(error_code)); + if (extra_code1) + event.SetKey("extracode1", base::Value(extra_code1)); + return event; +} + +std::vector<base::Value> Component::GetEvents() const { + std::vector<base::Value> events; + for (const auto& event : events_) + events.push_back(event.Clone()); + return events; +} + +Component::State::State(Component* component, ComponentState state) + : state_(state), component_(*component) {} + +Component::State::~State() {} + +void Component::State::Handle(CallbackNextState callback_next_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + + callback_next_state_ = std::move(callback_next_state); + + DoHandle(); +} + +void Component::State::TransitionState(std::unique_ptr<State> next_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(next_state); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(callback_next_state_), std::move(next_state))); +} + +void Component::State::EndState() { + DCHECK(thread_checker_.CalledOnValidThread()); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback_next_state_), nullptr)); +} + +Component::StateNew::StateNew(Component* component) + : State(component, ComponentState::kNew) {} + +Component::StateNew::~StateNew() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateNew::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = State::component(); + if (component.crx_component()) { + TransitionState(std::make_unique<StateChecking>(&component)); + } else { + component.error_code_ = static_cast<int>(Error::CRX_NOT_FOUND); + component.error_category_ = ErrorCategory::kService; + TransitionState(std::make_unique<StateUpdateError>(&component)); + } +} + +Component::StateChecking::StateChecking(Component* component) + : State(component, ComponentState::kChecking) {} + +Component::StateChecking::~StateChecking() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +// Unlike how other states are handled, this function does not change the +// state right away. The state transition happens when the UpdateChecker +// calls Component::UpdateCheckComplete and |update_check_complete_| is invoked. +// This is an artifact of how multiple components must be checked for updates +// together but the state machine defines the transitions for one component +// at a time. +void Component::StateChecking::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = State::component(); + DCHECK(component.crx_component()); + + component.last_check_ = base::TimeTicks::Now(); + component.update_check_complete_ = base::BindOnce( + &Component::StateChecking::UpdateCheckComplete, base::Unretained(this)); + + component.NotifyObservers(Events::COMPONENT_CHECKING_FOR_UPDATES); +} + +void Component::StateChecking::UpdateCheckComplete() { + DCHECK(thread_checker_.CalledOnValidThread()); + auto& component = State::component(); + if (!component.error_code_) { + if (component.status_ == "ok") { + TransitionState(std::make_unique<StateCanUpdate>(&component)); + return; + } + + if (component.status_ == "noupdate") { + if (component.action_run_.empty()) + TransitionState(std::make_unique<StateUpToDate>(&component)); + else + TransitionState(std::make_unique<StateRun>(&component)); + return; + } + } + + TransitionState(std::make_unique<StateUpdateError>(&component)); +} + +Component::StateUpdateError::StateUpdateError(Component* component) + : State(component, ComponentState::kUpdateError) {} + +Component::StateUpdateError::~StateUpdateError() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateUpdateError::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = State::component(); + + DCHECK_NE(ErrorCategory::kNone, component.error_category_); + DCHECK_NE(0, component.error_code_); + + // Create an event only when the server response included an update. + if (component.IsUpdateAvailable()) + component.AppendEvent(component.MakeEventUpdateComplete()); + + EndState(); + component.NotifyObservers(Events::COMPONENT_UPDATE_ERROR); +} + +Component::StateCanUpdate::StateCanUpdate(Component* component) + : State(component, ComponentState::kCanUpdate) {} + +Component::StateCanUpdate::~StateCanUpdate() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateCanUpdate::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = State::component(); + DCHECK(component.crx_component()); + + component.is_update_available_ = true; + component.NotifyObservers(Events::COMPONENT_UPDATE_FOUND); + + if (component.crx_component() + ->supports_group_policy_enable_component_updates && + !component.update_context_.enabled_component_updates) { + component.error_category_ = ErrorCategory::kService; + component.error_code_ = static_cast<int>(ServiceError::UPDATE_DISABLED); + component.extra_code1_ = 0; + TransitionState(std::make_unique<StateUpdateError>(&component)); + return; + } + + // Start computing the cost of the this update from here on. + component.update_begin_ = base::TimeTicks::Now(); + + if (CanTryDiffUpdate()) + TransitionState(std::make_unique<StateDownloadingDiff>(&component)); + else + TransitionState(std::make_unique<StateDownloading>(&component)); +} + +// Returns true if a differential update is available, it has not failed yet, +// and the configuration allows this update. +bool Component::StateCanUpdate::CanTryDiffUpdate() const { + const auto& component = Component::State::component(); + return HasDiffUpdate(component) && !component.diff_error_code_ && + component.update_context_.config->EnabledDeltas(); +} + +Component::StateUpToDate::StateUpToDate(Component* component) + : State(component, ComponentState::kUpToDate) {} + +Component::StateUpToDate::~StateUpToDate() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateUpToDate::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = State::component(); + DCHECK(component.crx_component()); + + component.NotifyObservers(Events::COMPONENT_NOT_UPDATED); + EndState(); +} + +Component::StateDownloadingDiff::StateDownloadingDiff(Component* component) + : State(component, ComponentState::kDownloadingDiff) {} + +Component::StateDownloadingDiff::~StateDownloadingDiff() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateDownloadingDiff::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + const auto& component = Component::State::component(); + const auto& update_context = component.update_context_; + + DCHECK(component.crx_component()); + + crx_downloader_ = update_context.crx_downloader_factory( + component.CanDoBackgroundDownload(), + update_context.config->GetNetworkFetcherFactory()); + + const auto& id = component.id_; + crx_downloader_->set_progress_callback( + base::Bind(&Component::StateDownloadingDiff::DownloadProgress, + base::Unretained(this), id)); + crx_downloader_->StartDownload( + component.crx_diffurls_, component.hashdiff_sha256_, + base::BindOnce(&Component::StateDownloadingDiff::DownloadComplete, + base::Unretained(this), id)); + + component.NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING); +} + +// Called when progress is being made downloading a CRX. Can be called multiple +// times due to how the CRX downloader switches between different downloaders +// and fallback urls. +void Component::StateDownloadingDiff::DownloadProgress(const std::string& id) { + DCHECK(thread_checker_.CalledOnValidThread()); + + component().NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING); +} + +void Component::StateDownloadingDiff::DownloadComplete( + const std::string& id, + const CrxDownloader::Result& download_result) { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = Component::State::component(); + for (const auto& download_metrics : crx_downloader_->download_metrics()) + component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics)); + + crx_downloader_.reset(); + + if (download_result.error) { + DCHECK(download_result.response.empty()); + component.diff_error_category_ = ErrorCategory::kDownload; + component.diff_error_code_ = download_result.error; + + TransitionState(std::make_unique<StateDownloading>(&component)); + return; + } + + component.crx_path_ = download_result.response; + + TransitionState(std::make_unique<StateUpdatingDiff>(&component)); +} + +Component::StateDownloading::StateDownloading(Component* component) + : State(component, ComponentState::kDownloading) {} + +Component::StateDownloading::~StateDownloading() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateDownloading::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + const auto& component = Component::State::component(); + const auto& update_context = component.update_context_; + + DCHECK(component.crx_component()); + + crx_downloader_ = update_context.crx_downloader_factory( + component.CanDoBackgroundDownload(), + update_context.config->GetNetworkFetcherFactory()); + + const auto& id = component.id_; + crx_downloader_->set_progress_callback( + base::Bind(&Component::StateDownloading::DownloadProgress, + base::Unretained(this), id)); + crx_downloader_->StartDownload( + component.crx_urls_, component.hash_sha256_, + base::BindOnce(&Component::StateDownloading::DownloadComplete, + base::Unretained(this), id)); + + component.NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING); +} + +// Called when progress is being made downloading a CRX. Can be called multiple +// times due to how the CRX downloader switches between different downloaders +// and fallback urls. +void Component::StateDownloading::DownloadProgress(const std::string& id) { + DCHECK(thread_checker_.CalledOnValidThread()); + + component().NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING); +} + +void Component::StateDownloading::DownloadComplete( + const std::string& id, + const CrxDownloader::Result& download_result) { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = Component::State::component(); + + for (const auto& download_metrics : crx_downloader_->download_metrics()) + component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics)); + + crx_downloader_.reset(); + + if (download_result.error) { + DCHECK(download_result.response.empty()); + component.error_category_ = ErrorCategory::kDownload; + component.error_code_ = download_result.error; + + TransitionState(std::make_unique<StateUpdateError>(&component)); + return; + } + + component.crx_path_ = download_result.response; + + TransitionState(std::make_unique<StateUpdating>(&component)); +} + +Component::StateUpdatingDiff::StateUpdatingDiff(Component* component) + : State(component, ComponentState::kUpdatingDiff) {} + +Component::StateUpdatingDiff::~StateUpdatingDiff() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateUpdatingDiff::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + const auto& component = Component::State::component(); + const auto& update_context = component.update_context_; + + DCHECK(component.crx_component()); + + component.NotifyObservers(Events::COMPONENT_UPDATE_READY); + + base::CreateSequencedTaskRunner(kTaskTraits) + ->PostTask( + FROM_HERE, + base::BindOnce( + &update_client::StartInstallOnBlockingTaskRunner, + base::ThreadTaskRunnerHandle::Get(), + component.crx_component()->pk_hash, component.crx_path_, + component.next_fp_, component.crx_component()->installer, + update_context.config->GetUnzipperFactory()->Create(), + update_context.config->GetPatcherFactory()->Create(), + component.crx_component()->crx_format_requirement, + base::BindOnce(&Component::StateUpdatingDiff::InstallComplete, + base::Unretained(this)))); +} + +void Component::StateUpdatingDiff::InstallComplete(ErrorCategory error_category, + int error_code, + int extra_code1) { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = Component::State::component(); + + component.diff_error_category_ = error_category; + component.diff_error_code_ = error_code; + component.diff_extra_code1_ = extra_code1; + + if (component.diff_error_code_ != 0) { + TransitionState(std::make_unique<StateDownloading>(&component)); + return; + } + + DCHECK_EQ(ErrorCategory::kNone, component.diff_error_category_); + DCHECK_EQ(0, component.diff_error_code_); + DCHECK_EQ(0, component.diff_extra_code1_); + + DCHECK_EQ(ErrorCategory::kNone, component.error_category_); + DCHECK_EQ(0, component.error_code_); + DCHECK_EQ(0, component.extra_code1_); + + if (component.action_run_.empty()) + TransitionState(std::make_unique<StateUpdated>(&component)); + else + TransitionState(std::make_unique<StateRun>(&component)); +} + +Component::StateUpdating::StateUpdating(Component* component) + : State(component, ComponentState::kUpdating) {} + +Component::StateUpdating::~StateUpdating() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateUpdating::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + const auto& component = Component::State::component(); + const auto& update_context = component.update_context_; + + DCHECK(component.crx_component()); + + component.NotifyObservers(Events::COMPONENT_UPDATE_READY); + + base::CreateSequencedTaskRunner(kTaskTraits) + ->PostTask(FROM_HERE, + base::BindOnce( + &update_client::StartInstallOnBlockingTaskRunner, + base::ThreadTaskRunnerHandle::Get(), + component.crx_component()->pk_hash, component.crx_path_, + component.next_fp_, component.crx_component()->installer, + update_context.config->GetUnzipperFactory()->Create(), + update_context.config->GetPatcherFactory()->Create(), + component.crx_component()->crx_format_requirement, + base::BindOnce(&Component::StateUpdating::InstallComplete, + base::Unretained(this)))); +} + +void Component::StateUpdating::InstallComplete(ErrorCategory error_category, + int error_code, + int extra_code1) { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = Component::State::component(); + + component.error_category_ = error_category; + component.error_code_ = error_code; + component.extra_code1_ = extra_code1; + + if (component.error_code_ != 0) { + TransitionState(std::make_unique<StateUpdateError>(&component)); + return; + } + + DCHECK_EQ(ErrorCategory::kNone, component.error_category_); + DCHECK_EQ(0, component.error_code_); + DCHECK_EQ(0, component.extra_code1_); + + if (component.action_run_.empty()) + TransitionState(std::make_unique<StateUpdated>(&component)); + else + TransitionState(std::make_unique<StateRun>(&component)); +} + +Component::StateUpdated::StateUpdated(Component* component) + : State(component, ComponentState::kUpdated) { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +Component::StateUpdated::~StateUpdated() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateUpdated::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = State::component(); + DCHECK(component.crx_component()); + + component.crx_component_->version = component.next_version_; + component.crx_component_->fingerprint = component.next_fp_; + + component.AppendEvent(component.MakeEventUpdateComplete()); + + component.NotifyObservers(Events::COMPONENT_UPDATED); + EndState(); +} + +Component::StateUninstalled::StateUninstalled(Component* component) + : State(component, ComponentState::kUninstalled) { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +Component::StateUninstalled::~StateUninstalled() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateUninstalled::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = State::component(); + DCHECK(component.crx_component()); + + component.AppendEvent(component.MakeEventUninstalled()); + + EndState(); +} + +Component::StateRun::StateRun(Component* component) + : State(component, ComponentState::kRun) {} + +Component::StateRun::~StateRun() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Component::StateRun::DoHandle() { + DCHECK(thread_checker_.CalledOnValidThread()); + + const auto& component = State::component(); + DCHECK(component.crx_component()); + + action_runner_ = std::make_unique<ActionRunner>(component); + action_runner_->Run( + base::BindOnce(&StateRun::ActionRunComplete, base::Unretained(this))); +} + +void Component::StateRun::ActionRunComplete(bool succeeded, + int error_code, + int extra_code1) { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto& component = State::component(); + + component.AppendEvent( + component.MakeEventActionRun(succeeded, error_code, extra_code1)); + switch (component.previous_state_) { + case ComponentState::kChecking: + TransitionState(std::make_unique<StateUpToDate>(&component)); + return; + case ComponentState::kUpdating: + case ComponentState::kUpdatingDiff: + TransitionState(std::make_unique<StateUpdated>(&component)); + return; + default: + break; + } + NOTREACHED(); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/component.h b/src/updater_components/update_client/component.h new file mode 100644 index 0000000..1e8ed14 --- /dev/null +++ b/src/updater_components/update_client/component.h
@@ -0,0 +1,462 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_H_ +#define COMPONENTS_UPDATE_CLIENT_COMPONENT_H_ + +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/optional.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "base/version.h" +#include "components/update_client/crx_downloader.h" +#include "components/update_client/protocol_parser.h" +#include "components/update_client/update_client.h" +#include "url/gurl.h" + +namespace base { +class Value; +} // namespace base + +namespace update_client { + +class ActionRunner; +class Configurator; +struct CrxUpdateItem; +struct UpdateContext; + +// Describes a CRX component managed by the UpdateEngine. Each |Component| is +// associated with an UpdateContext. +class Component { + public: + using Events = UpdateClient::Observer::Events; + + using CallbackHandleComplete = base::OnceCallback<void()>; + + Component(const UpdateContext& update_context, const std::string& id); + ~Component(); + + // Handles the current state of the component and makes it transition + // to the next component state before |callback_handle_complete_| is invoked. + void Handle(CallbackHandleComplete callback_handle_complete); + + CrxUpdateItem GetCrxUpdateItem() const; + + // Sets the uninstall state for this component. + void Uninstall(const base::Version& cur_version, int reason); + + // Called by the UpdateEngine when an update check for this component is done. + void SetUpdateCheckResult( + const base::Optional<ProtocolParser::Result>& result, + ErrorCategory error_category, + int error); + + // Returns true if the component has reached a final state and no further + // handling and state transitions are possible. + bool IsHandled() const { return is_handled_; } + + // Returns true if an update is available for this component, meaning that + // the update server has return a response containing an update. + bool IsUpdateAvailable() const { return is_update_available_; } + + base::TimeDelta GetUpdateDuration() const; + + ComponentState state() const { return state_->state(); } + + std::string id() const { return id_; } + + const base::Optional<CrxComponent>& crx_component() const { + return crx_component_; + } + void set_crx_component(const CrxComponent& crx_component) { + crx_component_ = crx_component; + } + + const base::Version& previous_version() const { return previous_version_; } + void set_previous_version(const base::Version& previous_version) { + previous_version_ = previous_version; + } + + const base::Version& next_version() const { return next_version_; } + + std::string previous_fp() const { return previous_fp_; } + void set_previous_fp(const std::string& previous_fp) { + previous_fp_ = previous_fp; + } + + std::string next_fp() const { return next_fp_; } + void set_next_fp(const std::string& next_fp) { next_fp_ = next_fp; } + + bool is_foreground() const; + + const std::vector<GURL>& crx_diffurls() const { return crx_diffurls_; } + + bool diff_update_failed() const { return !!diff_error_code_; } + + ErrorCategory error_category() const { return error_category_; } + int error_code() const { return error_code_; } + int extra_code1() const { return extra_code1_; } + ErrorCategory diff_error_category() const { return diff_error_category_; } + int diff_error_code() const { return diff_error_code_; } + int diff_extra_code1() const { return diff_extra_code1_; } + + std::string action_run() const { return action_run_; } + + scoped_refptr<Configurator> config() const; + + std::string session_id() const; + + const std::vector<base::Value>& events() const { return events_; } + + // Returns a clone of the component events. + std::vector<base::Value> GetEvents() const; + + private: + friend class MockPingManagerImpl; + friend class UpdateCheckerTest; + + FRIEND_TEST_ALL_PREFIXES(PingManagerTest, SendPing); + FRIEND_TEST_ALL_PREFIXES(PingManagerTest, RequiresEncryption); + FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, NoUpdateActionRun); + FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckCupError); + FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckError); + FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckInvalidAp); + FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, + UpdateCheckRequiresEncryptionError); + FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckSuccess); + FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckUpdateDisabled); + + // Describes an abstraction for implementing the behavior of a component and + // the transition from one state to another. + class State { + public: + using CallbackNextState = + base::OnceCallback<void(std::unique_ptr<State> next_state)>; + + State(Component* component, ComponentState state); + virtual ~State(); + + // Handles the current state and initiates a transition to a new state. + // The transition to the new state is non-blocking and it is completed + // by the outer component, after the current state is fully handled. + void Handle(CallbackNextState callback); + + ComponentState state() const { return state_; } + + protected: + // Initiates the transition to the new state. + void TransitionState(std::unique_ptr<State> new_state); + + // Makes the current state a final state where no other state transition + // can further occur. + void EndState(); + + Component& component() { return component_; } + const Component& component() const { return component_; } + + base::ThreadChecker thread_checker_; + + const ComponentState state_; + + private: + virtual void DoHandle() = 0; + + Component& component_; + CallbackNextState callback_next_state_; + }; + + class StateNew : public State { + public: + explicit StateNew(Component* component); + ~StateNew() override; + + private: + // State overrides. + void DoHandle() override; + + DISALLOW_COPY_AND_ASSIGN(StateNew); + }; + + class StateChecking : public State { + public: + explicit StateChecking(Component* component); + ~StateChecking() override; + + private: + // State overrides. + void DoHandle() override; + + void UpdateCheckComplete(); + + DISALLOW_COPY_AND_ASSIGN(StateChecking); + }; + + class StateUpdateError : public State { + public: + explicit StateUpdateError(Component* component); + ~StateUpdateError() override; + + private: + // State overrides. + void DoHandle() override; + + DISALLOW_COPY_AND_ASSIGN(StateUpdateError); + }; + + class StateCanUpdate : public State { + public: + explicit StateCanUpdate(Component* component); + ~StateCanUpdate() override; + + private: + // State overrides. + void DoHandle() override; + bool CanTryDiffUpdate() const; + + DISALLOW_COPY_AND_ASSIGN(StateCanUpdate); + }; + + class StateUpToDate : public State { + public: + explicit StateUpToDate(Component* component); + ~StateUpToDate() override; + + private: + // State overrides. + void DoHandle() override; + + DISALLOW_COPY_AND_ASSIGN(StateUpToDate); + }; + + class StateDownloadingDiff : public State { + public: + explicit StateDownloadingDiff(Component* component); + ~StateDownloadingDiff() override; + + private: + // State overrides. + void DoHandle() override; + + // Called when progress is being made downloading a CRX. Can be called + // multiple times due to how the CRX downloader switches between + // different downloaders and fallback urls. + void DownloadProgress(const std::string& id); + + void DownloadComplete(const std::string& id, + const CrxDownloader::Result& download_result); + + // Downloads updates for one CRX id only. + std::unique_ptr<CrxDownloader> crx_downloader_; + + DISALLOW_COPY_AND_ASSIGN(StateDownloadingDiff); + }; + + class StateDownloading : public State { + public: + explicit StateDownloading(Component* component); + ~StateDownloading() override; + + private: + // State overrides. + void DoHandle() override; + + // Called when progress is being made downloading a CRX. Can be called + // multiple times due to how the CRX downloader switches between + // different downloaders and fallback urls. + void DownloadProgress(const std::string& id); + + void DownloadComplete(const std::string& id, + const CrxDownloader::Result& download_result); + + // Downloads updates for one CRX id only. + std::unique_ptr<CrxDownloader> crx_downloader_; + + DISALLOW_COPY_AND_ASSIGN(StateDownloading); + }; + + class StateUpdatingDiff : public State { + public: + explicit StateUpdatingDiff(Component* component); + ~StateUpdatingDiff() override; + + private: + // State overrides. + void DoHandle() override; + + void InstallComplete(ErrorCategory error_category, + int error_code, + int extra_code1); + + DISALLOW_COPY_AND_ASSIGN(StateUpdatingDiff); + }; + + class StateUpdating : public State { + public: + explicit StateUpdating(Component* component); + ~StateUpdating() override; + + private: + // State overrides. + void DoHandle() override; + + void InstallComplete(ErrorCategory error_category, + int error_code, + int extra_code1); + + DISALLOW_COPY_AND_ASSIGN(StateUpdating); + }; + + class StateUpdated : public State { + public: + explicit StateUpdated(Component* component); + ~StateUpdated() override; + + private: + // State overrides. + void DoHandle() override; + + DISALLOW_COPY_AND_ASSIGN(StateUpdated); + }; + + class StateUninstalled : public State { + public: + explicit StateUninstalled(Component* component); + ~StateUninstalled() override; + + private: + // State overrides. + void DoHandle() override; + + DISALLOW_COPY_AND_ASSIGN(StateUninstalled); + }; + + class StateRun : public State { + public: + explicit StateRun(Component* component); + ~StateRun() override; + + private: + // State overrides. + void DoHandle() override; + + void ActionRunComplete(bool succeeded, int error_code, int extra_code1); + + // Runs the action referred by the |action_run_| member of the Component + // class. + std::unique_ptr<ActionRunner> action_runner_; + + DISALLOW_COPY_AND_ASSIGN(StateRun); + }; + + // Returns true is the update payload for this component can be downloaded + // by a downloader which can do bandwidth throttling on the client side. + bool CanDoBackgroundDownload() const; + + void AppendEvent(base::Value event); + + // Changes the component state and notifies the caller of the |Handle| + // function that the handling of this component state is complete. + void ChangeState(std::unique_ptr<State> next_state); + + // Notifies registered observers about changes in the state of the component. + void NotifyObservers(Events event) const; + + void SetParseResult(const ProtocolParser::Result& result); + + // These functions return a specific event. Each data member of the event is + // represented as a key-value pair in a dictionary value. + base::Value MakeEventUpdateComplete() const; + base::Value MakeEventDownloadMetrics( + const CrxDownloader::DownloadMetrics& download_metrics) const; + base::Value MakeEventUninstalled() const; + base::Value MakeEventActionRun(bool succeeded, + int error_code, + int extra_code1) const; + + base::ThreadChecker thread_checker_; + + const std::string id_; + base::Optional<CrxComponent> crx_component_; + + // The status of the updatecheck response. + std::string status_; + + // Time when an update check for this CRX has happened. + base::TimeTicks last_check_; + + // Time when the update of this CRX has begun. + base::TimeTicks update_begin_; + + // A component can be made available for download from several urls. + std::vector<GURL> crx_urls_; + std::vector<GURL> crx_diffurls_; + + // The cryptographic hash values for the component payload. + std::string hash_sha256_; + std::string hashdiff_sha256_; + + // The from/to version and fingerprint values. + base::Version previous_version_; + base::Version next_version_; + std::string previous_fp_; + std::string next_fp_; + + // Contains the file name of the payload to run. This member is set by + // the update response parser, when the update response includes a run action. + std::string action_run_; + + // True if the update check response for this component includes an update. + bool is_update_available_ = false; + + // The error reported by the update checker. + int update_check_error_ = 0; + + base::FilePath crx_path_; + + // The error information for full and differential updates. + // The |error_category| contains a hint about which module in the component + // updater generated the error. The |error_code| constains the error and + // the |extra_code1| usually contains a system error, but it can contain + // any extended information that is relevant to either the category or the + // error itself. + ErrorCategory error_category_ = ErrorCategory::kNone; + int error_code_ = 0; + int extra_code1_ = 0; + ErrorCategory diff_error_category_ = ErrorCategory::kNone; + int diff_error_code_ = 0; + int diff_extra_code1_ = 0; + + // Contains the events which are therefore serialized in the requests. + std::vector<base::Value> events_; + + CallbackHandleComplete callback_handle_complete_; + std::unique_ptr<State> state_; + const UpdateContext& update_context_; + + base::OnceClosure update_check_complete_; + + ComponentState previous_state_ = ComponentState::kLastStatus; + + // True if this component has reached a final state because all its states + // have been handled. + bool is_handled_ = false; + + DISALLOW_COPY_AND_ASSIGN(Component); +}; + +using IdToComponentPtrMap = std::map<std::string, std::unique_ptr<Component>>; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_COMPONENT_H_
diff --git a/src/updater_components/update_client/component_patcher.cc b/src/updater_components/update_client/component_patcher.cc new file mode 100644 index 0000000..ba81f1d --- /dev/null +++ b/src/updater_components/update_client/component_patcher.cc
@@ -0,0 +1,118 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/component_patcher.h" + +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/json/json_file_value_serializer.h" +#include "base/location.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/values.h" +#include "components/update_client/component_patcher_operation.h" +#include "components/update_client/patcher.h" +#include "components/update_client/update_client.h" +#include "components/update_client/update_client_errors.h" + +namespace update_client { + +namespace { + +// Deserialize the commands file (present in delta update packages). The top +// level must be a list. +base::ListValue* ReadCommands(const base::FilePath& unpack_path) { + const base::FilePath commands = + unpack_path.Append(FILE_PATH_LITERAL("commands.json")); + if (!base::PathExists(commands)) + return nullptr; + + JSONFileValueDeserializer deserializer(commands); + std::unique_ptr<base::Value> root = + deserializer.Deserialize(nullptr, nullptr); + + return (root.get() && root->is_list()) + ? static_cast<base::ListValue*>(root.release()) + : nullptr; +} + +} // namespace + +ComponentPatcher::ComponentPatcher(const base::FilePath& input_dir, + const base::FilePath& unpack_dir, + scoped_refptr<CrxInstaller> installer, + scoped_refptr<Patcher> patcher) + : input_dir_(input_dir), + unpack_dir_(unpack_dir), + installer_(installer), + patcher_(patcher) {} + +ComponentPatcher::~ComponentPatcher() { +} + +void ComponentPatcher::Start(Callback callback) { + callback_ = std::move(callback); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&ComponentPatcher::StartPatching, + scoped_refptr<ComponentPatcher>(this))); +} + +void ComponentPatcher::StartPatching() { + commands_.reset(ReadCommands(input_dir_)); + if (!commands_) { + DonePatching(UnpackerError::kDeltaBadCommands, 0); + } else { + next_command_ = commands_->begin(); + PatchNextFile(); + } +} + +void ComponentPatcher::PatchNextFile() { + if (next_command_ == commands_->end()) { + DonePatching(UnpackerError::kNone, 0); + return; + } + const base::DictionaryValue* command_args; + if (!next_command_->GetAsDictionary(&command_args)) { + DonePatching(UnpackerError::kDeltaBadCommands, 0); + return; + } + + std::string operation; + if (command_args->GetString(kOp, &operation)) { + current_operation_ = CreateDeltaUpdateOp(operation, patcher_); + } + + if (!current_operation_) { + DonePatching(UnpackerError::kDeltaUnsupportedCommand, 0); + return; + } + current_operation_->Run( + command_args, input_dir_, unpack_dir_, installer_, + base::BindOnce(&ComponentPatcher::DonePatchingFile, + scoped_refptr<ComponentPatcher>(this))); +} + +void ComponentPatcher::DonePatchingFile(UnpackerError error, + int extended_error) { + if (error != UnpackerError::kNone) { + DonePatching(error, extended_error); + } else { + ++next_command_; + PatchNextFile(); + } +} + +void ComponentPatcher::DonePatching(UnpackerError error, int extended_error) { + current_operation_ = nullptr; + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback_), error, extended_error)); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/component_patcher.h b/src/updater_components/update_client/component_patcher.h new file mode 100644 index 0000000..e31a812 --- /dev/null +++ b/src/updater_components/update_client/component_patcher.h
@@ -0,0 +1,103 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Component updates can be either differential updates or full updates. +// Full updates come in CRX format; differential updates come in CRX-style +// archives, but have a different magic number. They contain "commands.json", a +// list of commands for the patcher to follow. The patcher uses these commands, +// the other files in the archive, and the files from the existing installation +// of the component to create the contents of a full update, which is then +// installed normally. +// Component updates are specified by the 'codebasediff' attribute of an +// updatecheck response: +// <updatecheck codebase="http://example.com/extension_1.2.3.4.crx" +// hash="12345" size="9854" status="ok" version="1.2.3.4" +// prodversionmin="2.0.143.0" +// codebasediff="http://example.com/diff_1.2.3.4.crx" +// hashdiff="123" sizediff="101" +// fp="1.123"/> +// The component updater will attempt a differential update if it is available +// and allowed to, and fall back to a full update if it fails. +// +// After installation (diff or full), the component updater records "fp", the +// fingerprint of the installed files, to later identify the existing files to +// the server so that a proper differential update can be provided next cycle. + +#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_H_ +#define COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_H_ + +#include <memory> + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/values.h" +#include "components/update_client/component_unpacker.h" + +namespace base { +class FilePath; +} + +namespace update_client { + +class CrxInstaller; +class DeltaUpdateOp; +enum class UnpackerError; + +// The type of a patch file. +enum PatchType { + kPatchTypeUnknown, + kPatchTypeCourgette, + kPatchTypeBsdiff, +}; + +// Encapsulates a task for applying a differential update to a component. +class ComponentPatcher : public base::RefCountedThreadSafe<ComponentPatcher> { + public: + using Callback = base::OnceCallback<void(UnpackerError, int)>; + + // Takes an unpacked differential CRX (|input_dir|) and a component installer, + // and sets up the class to create a new (non-differential) unpacked CRX. + // If |in_process| is true, patching will be done completely within the + // existing process. Otherwise, some steps of patching may be done + // out-of-process. + ComponentPatcher(const base::FilePath& input_dir, + const base::FilePath& unpack_dir, + scoped_refptr<CrxInstaller> installer, + scoped_refptr<Patcher> patcher); + + // Starts patching files. This member function returns immediately, after + // posting a task to do the patching. When patching has been completed, + // |callback| will be called with the error codes if any error codes were + // encountered. + void Start(Callback callback); + + private: + friend class base::RefCountedThreadSafe<ComponentPatcher>; + + virtual ~ComponentPatcher(); + + void StartPatching(); + + void PatchNextFile(); + + void DonePatchingFile(UnpackerError error, int extended_error); + + void DonePatching(UnpackerError error, int extended_error); + + const base::FilePath input_dir_; + const base::FilePath unpack_dir_; + scoped_refptr<CrxInstaller> installer_; + scoped_refptr<Patcher> patcher_; + Callback callback_; + std::unique_ptr<base::ListValue> commands_; + base::ListValue::const_iterator next_command_; + scoped_refptr<DeltaUpdateOp> current_operation_; + + DISALLOW_COPY_AND_ASSIGN(ComponentPatcher); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_H_
diff --git a/src/updater_components/update_client/component_patcher_operation.cc b/src/updater_components/update_client/component_patcher_operation.cc new file mode 100644 index 0000000..c54a30e --- /dev/null +++ b/src/updater_components/update_client/component_patcher_operation.cc
@@ -0,0 +1,226 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/component_patcher_operation.h" + +#include <stdint.h> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/files/memory_mapped_file.h" +#include "base/location.h" +#include "base/strings/string_number_conversions.h" +#include "base/task/post_task.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "components/update_client/patcher.h" +#include "components/update_client/update_client.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/utils.h" +#include "courgette/courgette.h" +#include "courgette/third_party/bsdiff/bsdiff.h" + +namespace update_client { + +namespace { + +const char kOutput[] = "output"; +const char kSha256[] = "sha256"; + +// The integer offset disambiguates between overlapping error ranges. +const int kCourgetteErrorOffset = 300; +const int kBsdiffErrorOffset = 600; + +} // namespace + +const char kOp[] = "op"; +const char kBsdiff[] = "bsdiff"; +const char kCourgette[] = "courgette"; +const char kInput[] = "input"; +const char kPatch[] = "patch"; + +DeltaUpdateOp* CreateDeltaUpdateOp(const std::string& operation, + scoped_refptr<Patcher> patcher) { + if (operation == "copy") { + return new DeltaUpdateOpCopy(); + } else if (operation == "create") { + return new DeltaUpdateOpCreate(); + } else if (operation == "bsdiff" || operation == "courgette") { + return new DeltaUpdateOpPatch(operation, patcher); + } + return nullptr; +} + +DeltaUpdateOp::DeltaUpdateOp() { +} + +DeltaUpdateOp::~DeltaUpdateOp() { +} + +void DeltaUpdateOp::Run(const base::DictionaryValue* command_args, + const base::FilePath& input_dir, + const base::FilePath& unpack_dir, + scoped_refptr<CrxInstaller> installer, + ComponentPatcher::Callback callback) { + callback_ = std::move(callback); + std::string output_rel_path; + if (!command_args->GetString(kOutput, &output_rel_path) || + !command_args->GetString(kSha256, &output_sha256_)) { + DoneRunning(UnpackerError::kDeltaBadCommands, 0); + return; + } + + output_abs_path_ = + unpack_dir.Append(base::FilePath::FromUTF8Unsafe(output_rel_path)); + UnpackerError parse_result = + DoParseArguments(command_args, input_dir, installer); + if (parse_result != UnpackerError::kNone) { + DoneRunning(parse_result, 0); + return; + } + + const base::FilePath parent = output_abs_path_.DirName(); + if (!base::DirectoryExists(parent)) { + if (!base::CreateDirectory(parent)) { + DoneRunning(UnpackerError::kIoError, 0); + return; + } + } + + DoRun(base::BindOnce(&DeltaUpdateOp::DoneRunning, + scoped_refptr<DeltaUpdateOp>(this))); +} + +void DeltaUpdateOp::DoneRunning(UnpackerError error, int extended_error) { + if (error == UnpackerError::kNone) + error = CheckHash(); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback_), error, extended_error)); +} + +// Uses the hash as a checksum to confirm that the file now residing in the +// output directory probably has the contents it should. +UnpackerError DeltaUpdateOp::CheckHash() { + return VerifyFileHash256(output_abs_path_, output_sha256_) + ? UnpackerError::kNone + : UnpackerError::kDeltaVerificationFailure; +} + +DeltaUpdateOpCopy::DeltaUpdateOpCopy() { +} + +DeltaUpdateOpCopy::~DeltaUpdateOpCopy() { +} + +UnpackerError DeltaUpdateOpCopy::DoParseArguments( + const base::DictionaryValue* command_args, + const base::FilePath& input_dir, + scoped_refptr<CrxInstaller> installer) { + std::string input_rel_path; + if (!command_args->GetString(kInput, &input_rel_path)) + return UnpackerError::kDeltaBadCommands; + + if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) + return UnpackerError::kDeltaMissingExistingFile; + + return UnpackerError::kNone; +} + +void DeltaUpdateOpCopy::DoRun(ComponentPatcher::Callback callback) { + if (!base::CopyFile(input_abs_path_, output_abs_path_)) + std::move(callback).Run(UnpackerError::kDeltaOperationFailure, 0); + else + std::move(callback).Run(UnpackerError::kNone, 0); +} + +DeltaUpdateOpCreate::DeltaUpdateOpCreate() { +} + +DeltaUpdateOpCreate::~DeltaUpdateOpCreate() { +} + +UnpackerError DeltaUpdateOpCreate::DoParseArguments( + const base::DictionaryValue* command_args, + const base::FilePath& input_dir, + scoped_refptr<CrxInstaller> installer) { + std::string patch_rel_path; + if (!command_args->GetString(kPatch, &patch_rel_path)) + return UnpackerError::kDeltaBadCommands; + + patch_abs_path_ = + input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path)); + + return UnpackerError::kNone; +} + +void DeltaUpdateOpCreate::DoRun(ComponentPatcher::Callback callback) { + if (!base::Move(patch_abs_path_, output_abs_path_)) + std::move(callback).Run(UnpackerError::kDeltaOperationFailure, 0); + else + std::move(callback).Run(UnpackerError::kNone, 0); +} + +DeltaUpdateOpPatch::DeltaUpdateOpPatch(const std::string& operation, + scoped_refptr<Patcher> patcher) + : operation_(operation), patcher_(patcher) { + DCHECK(operation == kBsdiff || operation == kCourgette); +} + +DeltaUpdateOpPatch::~DeltaUpdateOpPatch() { +} + +UnpackerError DeltaUpdateOpPatch::DoParseArguments( + const base::DictionaryValue* command_args, + const base::FilePath& input_dir, + scoped_refptr<CrxInstaller> installer) { + std::string patch_rel_path; + std::string input_rel_path; + if (!command_args->GetString(kPatch, &patch_rel_path) || + !command_args->GetString(kInput, &input_rel_path)) + return UnpackerError::kDeltaBadCommands; + + if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) + return UnpackerError::kDeltaMissingExistingFile; + + patch_abs_path_ = + input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path)); + + return UnpackerError::kNone; +} + +void DeltaUpdateOpPatch::DoRun(ComponentPatcher::Callback callback) { + if (operation_ == kBsdiff) { + patcher_->PatchBsdiff(input_abs_path_, patch_abs_path_, output_abs_path_, + base::BindOnce(&DeltaUpdateOpPatch::DonePatching, + this, std::move(callback))); + } else { + patcher_->PatchCourgette(input_abs_path_, patch_abs_path_, output_abs_path_, + base::BindOnce(&DeltaUpdateOpPatch::DonePatching, + this, std::move(callback))); + } +} + +void DeltaUpdateOpPatch::DonePatching(ComponentPatcher::Callback callback, + int result) { + if (operation_ == kBsdiff) { + if (result == bsdiff::OK) { + std::move(callback).Run(UnpackerError::kNone, 0); + } else { + std::move(callback).Run(UnpackerError::kDeltaOperationFailure, + result + kBsdiffErrorOffset); + } + } else if (operation_ == kCourgette) { + if (result == courgette::C_OK) { + std::move(callback).Run(UnpackerError::kNone, 0); + } else { + std::move(callback).Run(UnpackerError::kDeltaOperationFailure, + result + kCourgetteErrorOffset); + } + } else { + NOTREACHED(); + } +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/component_patcher_operation.h b/src/updater_components/update_client/component_patcher_operation.h new file mode 100644 index 0000000..0649ff9 --- /dev/null +++ b/src/updater_components/update_client/component_patcher_operation.h
@@ -0,0 +1,163 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_OPERATION_H_ +#define COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_OPERATION_H_ + +#include <string> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/update_client/component_patcher.h" +#include "components/update_client/component_unpacker.h" + +namespace base { +class DictionaryValue; +} // namespace base + +namespace update_client { + +extern const char kOp[]; +extern const char kBsdiff[]; +extern const char kCourgette[]; +extern const char kInput[]; +extern const char kPatch[]; + +class CrxInstaller; +class Patcher; +enum class UnpackerError; + +class DeltaUpdateOp : public base::RefCountedThreadSafe<DeltaUpdateOp> { + public: + DeltaUpdateOp(); + + // Parses, runs, and verifies the operation. Calls |callback| with the + // result of the operation. The callback is called using |task_runner|. + void Run(const base::DictionaryValue* command_args, + const base::FilePath& input_dir, + const base::FilePath& unpack_dir, + scoped_refptr<CrxInstaller> installer, + ComponentPatcher::Callback callback); + + protected: + virtual ~DeltaUpdateOp(); + + std::string output_sha256_; + base::FilePath output_abs_path_; + + private: + friend class base::RefCountedThreadSafe<DeltaUpdateOp>; + + UnpackerError CheckHash(); + + // Subclasses must override DoParseArguments to parse operation-specific + // arguments. DoParseArguments returns DELTA_OK on success; any other code + // represents failure. + virtual UnpackerError DoParseArguments( + const base::DictionaryValue* command_args, + const base::FilePath& input_dir, + scoped_refptr<CrxInstaller> installer) = 0; + + // Subclasses must override DoRun to actually perform the patching operation. + // They must call the provided callback when they have completed their + // operations. In practice, the provided callback is always for "DoneRunning". + virtual void DoRun(ComponentPatcher::Callback callback) = 0; + + // Callback given to subclasses for when they complete their operation. + // Validates the output, and posts a task to the patching operation's + // callback. + void DoneRunning(UnpackerError error, int extended_error); + + ComponentPatcher::Callback callback_; + + DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOp); +}; + +// A 'copy' operation takes a file currently residing on the disk and moves it +// into the unpacking directory: this represents "no change" in the file being +// installed. +class DeltaUpdateOpCopy : public DeltaUpdateOp { + public: + DeltaUpdateOpCopy(); + + private: + ~DeltaUpdateOpCopy() override; + + // Overrides of DeltaUpdateOp. + UnpackerError DoParseArguments( + const base::DictionaryValue* command_args, + const base::FilePath& input_dir, + scoped_refptr<CrxInstaller> installer) override; + + void DoRun(ComponentPatcher::Callback callback) override; + + base::FilePath input_abs_path_; + + DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpCopy); +}; + +// A 'create' operation takes a full file that was sent in the delta update +// archive and moves it into the unpacking directory: this represents the +// addition of a new file, or a file so different that no bandwidth could be +// saved by transmitting a differential update. +class DeltaUpdateOpCreate : public DeltaUpdateOp { + public: + DeltaUpdateOpCreate(); + + private: + ~DeltaUpdateOpCreate() override; + + // Overrides of DeltaUpdateOp. + UnpackerError DoParseArguments( + const base::DictionaryValue* command_args, + const base::FilePath& input_dir, + scoped_refptr<CrxInstaller> installer) override; + + void DoRun(ComponentPatcher::Callback callback) override; + + base::FilePath patch_abs_path_; + + DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpCreate); +}; + +// Both 'bsdiff' and 'courgette' operations take an existing file on disk, +// and a bsdiff- or Courgette-format patch file provided in the delta update +// package, and run bsdiff or Courgette to construct an output file in the +// unpacking directory. +class DeltaUpdateOpPatch : public DeltaUpdateOp { + public: + DeltaUpdateOpPatch(const std::string& operation, + scoped_refptr<Patcher> patcher); + + private: + ~DeltaUpdateOpPatch() override; + + // Overrides of DeltaUpdateOp. + UnpackerError DoParseArguments( + const base::DictionaryValue* command_args, + const base::FilePath& input_dir, + scoped_refptr<CrxInstaller> installer) override; + + void DoRun(ComponentPatcher::Callback callback) override; + + // |success_code| is the code that indicates a successful patch. + // |result| is the code the patching operation returned. + void DonePatching(ComponentPatcher::Callback callback, int result); + + std::string operation_; + scoped_refptr<Patcher> patcher_; + base::FilePath patch_abs_path_; + base::FilePath input_abs_path_; + + DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpPatch); +}; + +DeltaUpdateOp* CreateDeltaUpdateOp(const std::string& operation, + scoped_refptr<Patcher> patcher); + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_OPERATION_H_
diff --git a/src/updater_components/update_client/component_patcher_unittest.cc b/src/updater_components/update_client/component_patcher_unittest.cc new file mode 100644 index 0000000..78b1cc8 --- /dev/null +++ b/src/updater_components/update_client/component_patcher_unittest.cc
@@ -0,0 +1,209 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/component_patcher.h" +#include "base/base_paths.h" +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/macros.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/values.h" +#include "components/services/patch/in_process_file_patcher.h" +#include "components/update_client/component_patcher_operation.h" +#include "components/update_client/component_patcher_unittest.h" +#include "components/update_client/patch/patch_impl.h" +#include "components/update_client/test_installer.h" +#include "components/update_client/update_client_errors.h" +#include "courgette/courgette.h" +#include "courgette/third_party/bsdiff/bsdiff.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class TestCallback { + public: + TestCallback(); + virtual ~TestCallback() {} + void Set(update_client::UnpackerError error, int extra_code); + + update_client::UnpackerError error_; + int extra_code_; + bool called_; + + private: + DISALLOW_COPY_AND_ASSIGN(TestCallback); +}; + +TestCallback::TestCallback() + : error_(update_client::UnpackerError::kNone), + extra_code_(-1), + called_(false) {} + +void TestCallback::Set(update_client::UnpackerError error, int extra_code) { + error_ = error; + extra_code_ = extra_code; + called_ = true; +} + +base::FilePath test_file(const char* file) { + base::FilePath path; + base::PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("components") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("update_client") + .AppendASCII(file); +} + +} // namespace + +namespace update_client { + +ComponentPatcherOperationTest::ComponentPatcherOperationTest() + : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) { + EXPECT_TRUE(unpack_dir_.CreateUniqueTempDir()); + EXPECT_TRUE(input_dir_.CreateUniqueTempDir()); + EXPECT_TRUE(installed_dir_.CreateUniqueTempDir()); + installer_ = + base::MakeRefCounted<ReadOnlyTestInstaller>(installed_dir_.GetPath()); +} + +ComponentPatcherOperationTest::~ComponentPatcherOperationTest() { +} + +// Verify that a 'create' delta update operation works correctly. +TEST_F(ComponentPatcherOperationTest, CheckCreateOperation) { + EXPECT_TRUE(base::CopyFile( + test_file("binary_output.bin"), + input_dir_.GetPath().Append(FILE_PATH_LITERAL("binary_output.bin")))); + + std::unique_ptr<base::DictionaryValue> command_args = + std::make_unique<base::DictionaryValue>(); + command_args->SetString("output", "output.bin"); + command_args->SetString("sha256", binary_output_hash); + command_args->SetString("op", "create"); + command_args->SetString("patch", "binary_output.bin"); + + TestCallback callback; + scoped_refptr<DeltaUpdateOp> op = base::MakeRefCounted<DeltaUpdateOpCreate>(); + op->Run(command_args.get(), input_dir_.GetPath(), unpack_dir_.GetPath(), + nullptr, + base::BindOnce(&TestCallback::Set, base::Unretained(&callback))); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(true, callback.called_); + EXPECT_EQ(UnpackerError::kNone, callback.error_); + EXPECT_EQ(0, callback.extra_code_); + EXPECT_TRUE(base::ContentsEqual( + unpack_dir_.GetPath().Append(FILE_PATH_LITERAL("output.bin")), + test_file("binary_output.bin"))); +} + +// Verify that a 'copy' delta update operation works correctly. +TEST_F(ComponentPatcherOperationTest, CheckCopyOperation) { + EXPECT_TRUE(base::CopyFile( + test_file("binary_output.bin"), + installed_dir_.GetPath().Append(FILE_PATH_LITERAL("binary_output.bin")))); + + std::unique_ptr<base::DictionaryValue> command_args = + std::make_unique<base::DictionaryValue>(); + command_args->SetString("output", "output.bin"); + command_args->SetString("sha256", binary_output_hash); + command_args->SetString("op", "copy"); + command_args->SetString("input", "binary_output.bin"); + + TestCallback callback; + scoped_refptr<DeltaUpdateOp> op = base::MakeRefCounted<DeltaUpdateOpCopy>(); + op->Run(command_args.get(), input_dir_.GetPath(), unpack_dir_.GetPath(), + installer_.get(), + base::BindOnce(&TestCallback::Set, base::Unretained(&callback))); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(true, callback.called_); + EXPECT_EQ(UnpackerError::kNone, callback.error_); + EXPECT_EQ(0, callback.extra_code_); + EXPECT_TRUE(base::ContentsEqual( + unpack_dir_.GetPath().Append(FILE_PATH_LITERAL("output.bin")), + test_file("binary_output.bin"))); +} + +// Verify that a 'courgette' delta update operation works correctly. +TEST_F(ComponentPatcherOperationTest, CheckCourgetteOperation) { + EXPECT_TRUE(base::CopyFile( + test_file("binary_input.bin"), + installed_dir_.GetPath().Append(FILE_PATH_LITERAL("binary_input.bin")))); + EXPECT_TRUE(base::CopyFile(test_file("binary_courgette_patch.bin"), + input_dir_.GetPath().Append(FILE_PATH_LITERAL( + "binary_courgette_patch.bin")))); + + std::unique_ptr<base::DictionaryValue> command_args = + std::make_unique<base::DictionaryValue>(); + command_args->SetString("output", "output.bin"); + command_args->SetString("sha256", binary_output_hash); + command_args->SetString("op", "courgette"); + command_args->SetString("input", "binary_input.bin"); + command_args->SetString("patch", "binary_courgette_patch.bin"); + + scoped_refptr<Patcher> patcher = + base::MakeRefCounted<PatchChromiumFactory>( + base::BindRepeating(&patch::LaunchInProcessFilePatcher)) + ->Create(); + + TestCallback callback; + scoped_refptr<DeltaUpdateOp> op = CreateDeltaUpdateOp("courgette", patcher); + op->Run(command_args.get(), input_dir_.GetPath(), unpack_dir_.GetPath(), + installer_.get(), + base::BindOnce(&TestCallback::Set, base::Unretained(&callback))); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(true, callback.called_); + EXPECT_EQ(UnpackerError::kNone, callback.error_); + EXPECT_EQ(0, callback.extra_code_); + EXPECT_TRUE(base::ContentsEqual( + unpack_dir_.GetPath().Append(FILE_PATH_LITERAL("output.bin")), + test_file("binary_output.bin"))); +} + +// Verify that a 'bsdiff' delta update operation works correctly. +TEST_F(ComponentPatcherOperationTest, CheckBsdiffOperation) { + EXPECT_TRUE(base::CopyFile( + test_file("binary_input.bin"), + installed_dir_.GetPath().Append(FILE_PATH_LITERAL("binary_input.bin")))); + EXPECT_TRUE(base::CopyFile(test_file("binary_bsdiff_patch.bin"), + input_dir_.GetPath().Append(FILE_PATH_LITERAL( + "binary_bsdiff_patch.bin")))); + + std::unique_ptr<base::DictionaryValue> command_args = + std::make_unique<base::DictionaryValue>(); + command_args->SetString("output", "output.bin"); + command_args->SetString("sha256", binary_output_hash); + command_args->SetString("op", "courgette"); + command_args->SetString("input", "binary_input.bin"); + command_args->SetString("patch", "binary_bsdiff_patch.bin"); + + // The operation needs a Patcher to access the PatchService. + scoped_refptr<Patcher> patcher = + base::MakeRefCounted<PatchChromiumFactory>( + base::BindRepeating(&patch::LaunchInProcessFilePatcher)) + ->Create(); + + TestCallback callback; + scoped_refptr<DeltaUpdateOp> op = CreateDeltaUpdateOp("bsdiff", patcher); + op->Run(command_args.get(), input_dir_.GetPath(), unpack_dir_.GetPath(), + installer_.get(), + base::BindOnce(&TestCallback::Set, base::Unretained(&callback))); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(true, callback.called_); + EXPECT_EQ(UnpackerError::kNone, callback.error_); + EXPECT_EQ(0, callback.extra_code_); + EXPECT_TRUE(base::ContentsEqual( + unpack_dir_.GetPath().Append(FILE_PATH_LITERAL("output.bin")), + test_file("binary_output.bin"))); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/component_patcher_unittest.h b/src/updater_components/update_client/component_patcher_unittest.h new file mode 100644 index 0000000..cb50e87 --- /dev/null +++ b/src/updater_components/update_client/component_patcher_unittest.h
@@ -0,0 +1,41 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_UNITTEST_H_ +#define COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_UNITTEST_H_ + +#include <memory> + +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/ref_counted.h" +#include "base/sequenced_task_runner.h" +#include "base/test/task_environment.h" +#include "courgette/courgette.h" +#include "courgette/third_party/bsdiff/bsdiff.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace update_client { + +class ReadOnlyTestInstaller; + +const char binary_output_hash[] = + "599aba6d15a7da390621ef1bacb66601ed6aed04dadc1f9b445dcfe31296142a"; + +class ComponentPatcherOperationTest : public testing::Test { + public: + ComponentPatcherOperationTest(); + ~ComponentPatcherOperationTest() override; + + protected: + base::test::TaskEnvironment task_environment_; + base::ScopedTempDir input_dir_; + base::ScopedTempDir installed_dir_; + base::ScopedTempDir unpack_dir_; + scoped_refptr<ReadOnlyTestInstaller> installer_; +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_UNITTEST_H_
diff --git a/src/updater_components/update_client/component_unpacker.cc b/src/updater_components/update_client/component_unpacker.cc new file mode 100644 index 0000000..a9f34b8 --- /dev/null +++ b/src/updater_components/update_client/component_unpacker.cc
@@ -0,0 +1,155 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/component_unpacker.h" + +#include <stdint.h> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_file.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "components/crx_file/crx_verifier.h" +#include "components/update_client/component_patcher.h" +#include "components/update_client/patcher.h" +#include "components/update_client/unzipper.h" +#include "components/update_client/update_client.h" +#include "components/update_client/update_client_errors.h" + +namespace update_client { + +ComponentUnpacker::Result::Result() {} + +ComponentUnpacker::ComponentUnpacker(const std::vector<uint8_t>& pk_hash, + const base::FilePath& path, + scoped_refptr<CrxInstaller> installer, + std::unique_ptr<Unzipper> unzipper, + scoped_refptr<Patcher> patcher, + crx_file::VerifierFormat crx_format) + : pk_hash_(pk_hash), + path_(path), + is_delta_(false), + installer_(installer), + unzipper_(std::move(unzipper)), + patcher_tool_(patcher), + crx_format_(crx_format), + error_(UnpackerError::kNone), + extended_error_(0) {} + +ComponentUnpacker::~ComponentUnpacker() {} + +void ComponentUnpacker::Unpack(Callback callback) { + callback_ = std::move(callback); + if (!Verify() || !BeginUnzipping()) + EndUnpacking(); +} + +bool ComponentUnpacker::Verify() { + VLOG(1) << "Verifying component: " << path_.value(); + if (pk_hash_.empty() || path_.empty()) { + error_ = UnpackerError::kInvalidParams; + return false; + } + const std::vector<std::vector<uint8_t>> required_keys = {pk_hash_}; + const crx_file::VerifierResult result = + crx_file::Verify(path_, crx_format_, required_keys, + std::vector<uint8_t>(), &public_key_, nullptr); + if (result != crx_file::VerifierResult::OK_FULL && + result != crx_file::VerifierResult::OK_DELTA) { + error_ = UnpackerError::kInvalidFile; + extended_error_ = static_cast<int>(result); + return false; + } + is_delta_ = result == crx_file::VerifierResult::OK_DELTA; + VLOG(1) << "Verification successful: " << path_.value(); + return true; +} + +bool ComponentUnpacker::BeginUnzipping() { + // Mind the reference to non-const type, passed as an argument below. + base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_; + if (!base::CreateNewTempDirectory(base::FilePath::StringType(), + &destination)) { + VLOG(1) << "Unable to create temporary directory for unpacking."; + error_ = UnpackerError::kUnzipPathError; + return false; + } + VLOG(1) << "Unpacking in: " << destination.value(); + unzipper_->Unzip(path_, destination, + base::BindOnce(&ComponentUnpacker::EndUnzipping, this)); + return true; +} + +void ComponentUnpacker::EndUnzipping(bool result) { + if (!result) { + VLOG(1) << "Unzipping failed."; + error_ = UnpackerError::kUnzipFailed; + EndUnpacking(); + return; + } + VLOG(1) << "Unpacked successfully"; + BeginPatching(); +} + +bool ComponentUnpacker::BeginPatching() { + if (is_delta_) { // Package is a diff package. + // Use a different temp directory for the patch output files. + if (!base::CreateNewTempDirectory(base::FilePath::StringType(), + &unpack_path_)) { + error_ = UnpackerError::kUnzipPathError; + return false; + } + patcher_ = base::MakeRefCounted<ComponentPatcher>( + unpack_diff_path_, unpack_path_, installer_, patcher_tool_); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&ComponentPatcher::Start, patcher_, + base::BindOnce(&ComponentUnpacker::EndPatching, + scoped_refptr<ComponentUnpacker>(this)))); + } else { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&ComponentUnpacker::EndPatching, + scoped_refptr<ComponentUnpacker>(this), + UnpackerError::kNone, 0)); + } + return true; +} + +void ComponentUnpacker::EndPatching(UnpackerError error, int extended_error) { + error_ = error; + extended_error_ = extended_error; + patcher_ = nullptr; + + EndUnpacking(); +} + +void ComponentUnpacker::EndUnpacking() { + if (!unpack_diff_path_.empty()) + base::DeleteFile(unpack_diff_path_, true); + if (error_ != UnpackerError::kNone && !unpack_path_.empty()) + base::DeleteFile(unpack_path_, true); + + Result result; + result.error = error_; + result.extended_error = extended_error_; + if (error_ == UnpackerError::kNone) { + result.unpack_path = unpack_path_; + result.public_key = public_key_; + } + + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback_), result)); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/component_unpacker.h b/src/updater_components/update_client/component_unpacker.h new file mode 100644 index 0000000..1d79891 --- /dev/null +++ b/src/updater_components/update_client/component_unpacker.h
@@ -0,0 +1,148 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_UNPACKER_H_ +#define COMPONENTS_UPDATE_CLIENT_COMPONENT_UNPACKER_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/update_client/update_client_errors.h" + +namespace crx_file { +enum class VerifierFormat; +} + +namespace update_client { + +class CrxInstaller; +class ComponentPatcher; +class Patcher; +class Unzipper; + +// In charge of unpacking the component CRX package and verifying that it is +// well formed and the cryptographic signature is correct. +// +// This class should be used only by the component updater. It is inspired by +// and overlaps with code in the extension's SandboxedUnpacker. +// The main differences are: +// - The public key hash is full SHA256. +// - Does not use a sandboxed unpacker. A valid component is fully trusted. +// - The manifest can have different attributes and resources are not +// transcoded. +// +// If the CRX is a delta CRX, the flow is: +// [ComponentUpdater] [ComponentPatcher] +// Unpack +// \_ Verify +// \_ Unzip +// \_ BeginPatching ---> DifferentialUpdatePatch +// ... +// EndPatching <------------ ... +// \_ EndUnpacking +// +// For a full CRX, the flow is: +// [ComponentUpdater] +// Unpack +// \_ Verify +// \_ Unzip +// \_ BeginPatching +// | +// V +// EndPatching +// \_ EndUnpacking +// +// In both cases, if there is an error at any point, the remaining steps will +// be skipped and EndUnpacking will be called. +class ComponentUnpacker : public base::RefCountedThreadSafe<ComponentUnpacker> { + public: + // Contains the result of the unpacking. + struct Result { + Result(); + + // Unpack error: 0 indicates success. + UnpackerError error = UnpackerError::kNone; + + // Additional error information, such as errno or last error. + int extended_error = 0; + + // Path of the unpacked files if the unpacking was successful. + base::FilePath unpack_path; + + // The extracted public key of the package if the unpacking was successful. + std::string public_key; + }; + + using Callback = base::OnceCallback<void(const Result& result)>; + + // Constructs an unpacker for a specific component unpacking operation. + // |pk_hash| is the expected/ public key SHA256 hash. |path| is the current + // location of the CRX. + ComponentUnpacker(const std::vector<uint8_t>& pk_hash, + const base::FilePath& path, + scoped_refptr<CrxInstaller> installer, + std::unique_ptr<Unzipper> unzipper, + scoped_refptr<Patcher> patcher, + crx_file::VerifierFormat crx_format); + + // Begins the actual unpacking of the files. May invoke a patcher and the + // component installer if the package is a differential update. + // Calls |callback| with the result. + void Unpack(Callback callback); + + private: + friend class base::RefCountedThreadSafe<ComponentUnpacker>; + + virtual ~ComponentUnpacker(); + + // The first step of unpacking is to verify the file. Returns false if an + // error is encountered, the file is malformed, or the file is incorrectly + // signed. + bool Verify(); + + // The second step of unpacking is to unzip. Returns false if an early error + // is encountered. + bool BeginUnzipping(); + void EndUnzipping(bool error); + + // The third step is to optionally patch files - this is a no-op for full + // (non-differential) updates. This step is asynchronous. Returns false if an + // error is encountered. + bool BeginPatching(); + void EndPatching(UnpackerError error, int extended_error); + + // The final step is to do clean-up for things that can't be tidied as we go. + // If there is an error at any step, the remaining steps are skipped and + // EndUnpacking is called. EndUnpacking is responsible for calling the + // callback provided in Unpack(). + void EndUnpacking(); + + std::vector<uint8_t> pk_hash_; + base::FilePath path_; + base::FilePath unpack_path_; + base::FilePath unpack_diff_path_; + bool is_delta_; + scoped_refptr<ComponentPatcher> patcher_; + scoped_refptr<CrxInstaller> installer_; + Callback callback_; + std::unique_ptr<Unzipper> unzipper_; + scoped_refptr<Patcher> patcher_tool_; + crx_file::VerifierFormat crx_format_; + UnpackerError error_; + int extended_error_; + std::string public_key_; + + DISALLOW_COPY_AND_ASSIGN(ComponentUnpacker); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_COMPONENT_UNPACKER_H_
diff --git a/src/updater_components/update_client/component_unpacker_unittest.cc b/src/updater_components/update_client/component_unpacker_unittest.cc new file mode 100644 index 0000000..0bc1905 --- /dev/null +++ b/src/updater_components/update_client/component_unpacker_unittest.cc
@@ -0,0 +1,175 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <iterator> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/task/post_task.h" +#include "base/test/task_environment.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/crx_file/crx_verifier.h" +#include "components/update_client/component_unpacker.h" +#include "components/update_client/patcher.h" +#include "components/update_client/test_configurator.h" +#include "components/update_client/test_installer.h" +#include "components/update_client/unzipper.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class TestCallback { + public: + TestCallback(); + virtual ~TestCallback() {} + void Set(update_client::UnpackerError error, int extra_code); + + update_client::UnpackerError error_; + int extra_code_; + bool called_; + + private: + DISALLOW_COPY_AND_ASSIGN(TestCallback); +}; + +TestCallback::TestCallback() + : error_(update_client::UnpackerError::kNone), + extra_code_(-1), + called_(false) {} + +void TestCallback::Set(update_client::UnpackerError error, int extra_code) { + error_ = error; + extra_code_ = extra_code; + called_ = true; +} + +base::FilePath test_file(const char* file) { + base::FilePath path; + base::PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("components") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("update_client") + .AppendASCII(file); +} + +} // namespace + +namespace update_client { + +class ComponentUnpackerTest : public testing::Test { + public: + ComponentUnpackerTest(); + ~ComponentUnpackerTest() override; + + void UnpackComplete(const ComponentUnpacker::Result& result); + + protected: + void RunThreads(); + + base::test::TaskEnvironment task_environment_; + const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_ = + base::ThreadTaskRunnerHandle::Get(); + base::RunLoop runloop_; + base::OnceClosure quit_closure_ = runloop_.QuitClosure(); + + ComponentUnpacker::Result result_; +}; + +ComponentUnpackerTest::ComponentUnpackerTest() = default; + +ComponentUnpackerTest::~ComponentUnpackerTest() = default; + +void ComponentUnpackerTest::RunThreads() { + runloop_.Run(); +} + +void ComponentUnpackerTest::UnpackComplete( + const ComponentUnpacker::Result& result) { + result_ = result; + main_thread_task_runner_->PostTask(FROM_HERE, std::move(quit_closure_)); +} + +TEST_F(ComponentUnpackerTest, UnpackFullCrx) { + auto config = base::MakeRefCounted<TestConfigurator>(); + scoped_refptr<ComponentUnpacker> component_unpacker = + base::MakeRefCounted<ComponentUnpacker>( + std::vector<uint8_t>(std::begin(jebg_hash), std::end(jebg_hash)), + test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"), nullptr, + config->GetUnzipperFactory()->Create(), + config->GetPatcherFactory()->Create(), + crx_file::VerifierFormat::CRX2_OR_CRX3); + component_unpacker->Unpack(base::BindOnce( + &ComponentUnpackerTest::UnpackComplete, base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(UnpackerError::kNone, result_.error); + EXPECT_EQ(0, result_.extended_error); + + base::FilePath unpack_path = result_.unpack_path; + EXPECT_FALSE(unpack_path.empty()); + EXPECT_TRUE(base::DirectoryExists(unpack_path)); + EXPECT_EQ(jebg_public_key, result_.public_key); + + int64_t file_size = 0; + EXPECT_TRUE( + base::GetFileSize(unpack_path.AppendASCII("component1.dll"), &file_size)); + EXPECT_EQ(1024, file_size); + EXPECT_TRUE( + base::GetFileSize(unpack_path.AppendASCII("flashtest.pem"), &file_size)); + EXPECT_EQ(911, file_size); + EXPECT_TRUE( + base::GetFileSize(unpack_path.AppendASCII("manifest.json"), &file_size)); + EXPECT_EQ(144, file_size); + + EXPECT_TRUE(base::DeleteFile(unpack_path, true)); +} + +TEST_F(ComponentUnpackerTest, UnpackFileNotFound) { + scoped_refptr<ComponentUnpacker> component_unpacker = + base::MakeRefCounted<ComponentUnpacker>( + std::vector<uint8_t>(std::begin(jebg_hash), std::end(jebg_hash)), + test_file("file-not-found.crx"), nullptr, nullptr, nullptr, + crx_file::VerifierFormat::CRX2_OR_CRX3); + component_unpacker->Unpack(base::BindOnce( + &ComponentUnpackerTest::UnpackComplete, base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(UnpackerError::kInvalidFile, result_.error); + EXPECT_EQ(static_cast<int>(crx_file::VerifierResult::ERROR_FILE_NOT_READABLE), + result_.extended_error); + + EXPECT_TRUE(result_.unpack_path.empty()); +} + +// Tests a mismatch between the public key hash and the id of the component. +TEST_F(ComponentUnpackerTest, UnpackFileHashMismatch) { + scoped_refptr<ComponentUnpacker> component_unpacker = + base::MakeRefCounted<ComponentUnpacker>( + std::vector<uint8_t>(std::begin(abag_hash), std::end(abag_hash)), + test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"), nullptr, nullptr, + nullptr, crx_file::VerifierFormat::CRX2_OR_CRX3); + component_unpacker->Unpack(base::BindOnce( + &ComponentUnpackerTest::UnpackComplete, base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(UnpackerError::kInvalidFile, result_.error); + EXPECT_EQ( + static_cast<int>(crx_file::VerifierResult::ERROR_REQUIRED_PROOF_MISSING), + result_.extended_error); + + EXPECT_TRUE(result_.unpack_path.empty()); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/configurator.h b/src/updater_components/update_client/configurator.h new file mode 100644 index 0000000..f2ea5a2 --- /dev/null +++ b/src/updater_components/update_client/configurator.h
@@ -0,0 +1,177 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_CONFIGURATOR_H_ +#define COMPONENTS_UPDATE_CLIENT_CONFIGURATOR_H_ + +#include <memory> +#include <string> +#include <tuple> +#include <vector> + +#include "base/callback_forward.h" +#include "base/containers/flat_map.h" +#include "base/memory/ref_counted.h" + +class GURL; +class PrefService; + +namespace base { +class FilePath; +class Version; +} + +namespace update_client { + +class ActivityDataService; +class NetworkFetcherFactory; +class PatcherFactory; +class ProtocolHandlerFactory; +class UnzipperFactory; + +using RecoveryCRXElevator = base::OnceCallback<std::tuple<bool, int, int>( + const base::FilePath& crx_path, + const std::string& browser_appid, + const std::string& browser_version, + const std::string& session_id)>; + +// Controls the component updater behavior. +// TODO(sorin): this class will be split soon in two. One class controls +// the behavior of the update client, and the other class controls the +// behavior of the component updater. +class Configurator : public base::RefCountedThreadSafe<Configurator> { + public: + // Delay in seconds from calling Start() to the first update check. + virtual int InitialDelay() const = 0; + + // Delay in seconds to every subsequent update check. 0 means don't check. + virtual int NextCheckDelay() const = 0; + + // Minimum delta time in seconds before an on-demand check is allowed + // for the same component. + virtual int OnDemandDelay() const = 0; + + // The time delay in seconds between applying updates for different + // components. + virtual int UpdateDelay() const = 0; + + // The URLs for the update checks. The URLs are tried in order, the first one + // that succeeds wins. + virtual std::vector<GURL> UpdateUrl() const = 0; + + // The URLs for pings. Returns an empty vector if and only if pings are + // disabled. Similarly, these URLs have a fall back behavior too. + virtual std::vector<GURL> PingUrl() const = 0; + + // The ProdId is used as a prefix in some of the version strings which appear + // in the protocol requests. Possible values include "chrome", "chromecrx", + // "chromiumcrx", and "unknown". + virtual std::string GetProdId() const = 0; + + // Version of the application. Used to compare the component manifests. + virtual base::Version GetBrowserVersion() const = 0; + + // Returns the value we use for the "updaterchannel=" and "prodchannel=" + // parameters. Possible return values include: "canary", "dev", "beta", and + // "stable". + virtual std::string GetChannel() const = 0; + + // Returns the brand code or distribution tag that has been assigned to + // a partner. A brand code is a 4-character string used to identify + // installations that took place as a result of partner deals or website + // promotions. + virtual std::string GetBrand() const = 0; + + // Returns the language for the present locale. Possible return values are + // standard tags for languages, such as "en", "en-US", "de", "fr", "af", etc. + virtual std::string GetLang() const = 0; + + // Returns the OS's long name like "Windows", "Mac OS X", etc. + virtual std::string GetOSLongName() const = 0; + + // Parameters added to each url request. It can be empty if none are needed. + // Returns a map of name-value pairs that match ^[-_a-zA-Z0-9]$ regex. + virtual base::flat_map<std::string, std::string> ExtraRequestParams() + const = 0; + + // Provides a hint for the server to control the order in which multiple + // download urls are returned. The hint may or may not be honored in the + // response returned by the server. + // Returns an empty string if no policy is in effect. + virtual std::string GetDownloadPreference() const = 0; + + virtual scoped_refptr<NetworkFetcherFactory> GetNetworkFetcherFactory() = 0; + + virtual scoped_refptr<UnzipperFactory> GetUnzipperFactory() = 0; + + virtual scoped_refptr<PatcherFactory> GetPatcherFactory() = 0; + + // True means that this client can handle delta updates. + virtual bool EnabledDeltas() const = 0; + + // True if component updates are enabled. Updates for all components are + // enabled by default. This method allows enabling or disabling + // updates for certain components such as the plugins. Updates for some + // components are always enabled and can't be disabled programatically. + virtual bool EnabledComponentUpdates() const = 0; + + // True means that the background downloader can be used for downloading + // non on-demand components. + virtual bool EnabledBackgroundDownloader() const = 0; + + // True if signing of update checks is enabled. + virtual bool EnabledCupSigning() const = 0; + + // Returns a PrefService that the update_client can use to store persistent + // update information. The PrefService must outlive the entire update_client, + // and be safe to access from the thread the update_client is constructed + // on. + // Returning null is safe and will disable any functionality that requires + // persistent storage. + virtual PrefService* GetPrefService() const = 0; + + // Returns an ActivityDataService that the update_client can use to access + // to update information (namely active bit, last active/rollcall days) + // normally stored in the user extension profile. + // Similar to PrefService, ActivityDataService must outlive the entire + // update_client, and be safe to access from the thread the update_client + // is constructed on. + // Returning null is safe and will disable any functionality that requires + // accessing to the information provided by ActivityDataService. + virtual ActivityDataService* GetActivityDataService() const = 0; + + // Returns true if the Chrome is installed for the current user only, or false + // if Chrome is installed for all users on the machine. This function must be + // called only from a blocking pool thread, as it may access the file system. + virtual bool IsPerUserInstall() const = 0; + + // Returns the key hash corresponding to a CRX trusted by ActionRun. The + // CRX payloads are signed with this key, and their integrity is verified + // during the unpacking by the action runner. This is a dependency injection + // feature to support testing. + virtual std::vector<uint8_t> GetRunActionKeyHash() const = 0; + + // Returns the app GUID with which Chrome is registered with Google Update, or + // an empty string if this brand does not integrate with Google Update. + virtual std::string GetAppGuid() const = 0; + + // Returns the class factory to create protocol parser and protocol + // serializer object instances. + virtual std::unique_ptr<ProtocolHandlerFactory> GetProtocolHandlerFactory() + const = 0; + + // Returns a callback which can elevate and run the CRX payload associated + // with the improved recovery component. Running this payload repairs the + // Chrome update functionality. + virtual RecoveryCRXElevator GetRecoveryCRXElevator() const = 0; + + protected: + friend class base::RefCountedThreadSafe<Configurator>; + + virtual ~Configurator() {} +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_CONFIGURATOR_H_
diff --git a/src/updater_components/update_client/crx_downloader.cc b/src/updater_components/update_client/crx_downloader.cc new file mode 100644 index 0000000..cf45d4c --- /dev/null +++ b/src/updater_components/update_client/crx_downloader.cc
@@ -0,0 +1,214 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/crx_downloader.h" + +#include <utility> + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#if defined(OS_WIN) +#include "components/update_client/background_downloader_win.h" +#endif +#include "components/update_client/network.h" +#include "components/update_client/task_traits.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/url_fetcher_downloader.h" +#include "components/update_client/utils.h" + +namespace update_client { + +CrxDownloader::DownloadMetrics::DownloadMetrics() + : downloader(kNone), + error(0), + downloaded_bytes(-1), + total_bytes(-1), + download_time_ms(0) { +} + +// On Windows, the first downloader in the chain is a background downloader, +// which uses the BITS service. +std::unique_ptr<CrxDownloader> CrxDownloader::Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + std::unique_ptr<CrxDownloader> url_fetcher_downloader = + std::make_unique<UrlFetcherDownloader>(nullptr, network_fetcher_factory); + +#if defined(OS_WIN) + if (is_background_download) { + return std::make_unique<BackgroundDownloader>( + std::move(url_fetcher_downloader)); + } +#endif + + return url_fetcher_downloader; +} + +CrxDownloader::CrxDownloader(std::unique_ptr<CrxDownloader> successor) + : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), + successor_(std::move(successor)) {} + +CrxDownloader::~CrxDownloader() {} + +void CrxDownloader::set_progress_callback( + const ProgressCallback& progress_callback) { + progress_callback_ = progress_callback; +} + +GURL CrxDownloader::url() const { + return current_url_ != urls_.end() ? *current_url_ : GURL(); +} + +const std::vector<CrxDownloader::DownloadMetrics> +CrxDownloader::download_metrics() const { + if (!successor_) + return download_metrics_; + + std::vector<DownloadMetrics> retval(successor_->download_metrics()); + retval.insert(retval.begin(), download_metrics_.begin(), + download_metrics_.end()); + return retval; +} + +void CrxDownloader::StartDownloadFromUrl(const GURL& url, + const std::string& expected_hash, + DownloadCallback download_callback) { + std::vector<GURL> urls; + urls.push_back(url); + StartDownload(urls, expected_hash, std::move(download_callback)); +} + +void CrxDownloader::StartDownload(const std::vector<GURL>& urls, + const std::string& expected_hash, + DownloadCallback download_callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto error = CrxDownloaderError::NONE; + if (urls.empty()) { + error = CrxDownloaderError::NO_URL; + } else if (expected_hash.empty()) { + error = CrxDownloaderError::NO_HASH; + } + + if (error != CrxDownloaderError::NONE) { + Result result; + result.error = static_cast<int>(error); + main_task_runner()->PostTask( + FROM_HERE, base::BindOnce(std::move(download_callback), result)); + return; + } + + urls_ = urls; + expected_hash_ = expected_hash; + current_url_ = urls_.begin(); + download_callback_ = std::move(download_callback); + + DoStartDownload(*current_url_); +} + +void CrxDownloader::OnDownloadComplete( + bool is_handled, + const Result& result, + const DownloadMetrics& download_metrics) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!result.error) + base::PostTask( + FROM_HERE, kTaskTraits, + base::BindOnce(&CrxDownloader::VerifyResponse, base::Unretained(this), + is_handled, result, download_metrics)); + else + main_task_runner()->PostTask( + FROM_HERE, base::BindOnce(&CrxDownloader::HandleDownloadError, + base::Unretained(this), is_handled, result, + download_metrics)); +} + +void CrxDownloader::OnDownloadProgress() { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (progress_callback_.is_null()) + return; + + progress_callback_.Run(); +} + +// The function mutates the values of the parameters |result| and +// |download_metrics|. +void CrxDownloader::VerifyResponse(bool is_handled, + Result result, + DownloadMetrics download_metrics) { + DCHECK_EQ(0, result.error); + DCHECK_EQ(0, download_metrics.error); + DCHECK(is_handled); + + if (VerifyFileHash256(result.response, expected_hash_)) { + download_metrics_.push_back(download_metrics); + main_task_runner()->PostTask( + FROM_HERE, base::BindOnce(std::move(download_callback_), result)); + return; + } + + // The download was successful but the response is not trusted. Clean up + // the download, mutate the result, and try the remaining fallbacks when + // handling the error. + result.error = static_cast<int>(CrxDownloaderError::BAD_HASH); + download_metrics.error = result.error; + DeleteFileAndEmptyParentDirectory(result.response); + result.response.clear(); + + main_task_runner()->PostTask( + FROM_HERE, base::BindOnce(&CrxDownloader::HandleDownloadError, + base::Unretained(this), is_handled, result, + download_metrics)); +} + +void CrxDownloader::HandleDownloadError( + bool is_handled, + const Result& result, + const DownloadMetrics& download_metrics) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_NE(0, result.error); + DCHECK(result.response.empty()); + DCHECK_NE(0, download_metrics.error); + + download_metrics_.push_back(download_metrics); + + // If an error has occured, try the next url if there is any, + // or try the successor in the chain if there is any successor. + // If this downloader has received a 5xx error for the current url, + // as indicated by the |is_handled| flag, remove that url from the list of + // urls so the url is never tried again down the chain. + if (is_handled) { + current_url_ = urls_.erase(current_url_); + } else { + ++current_url_; + } + + // Try downloading from another url from the list. + if (current_url_ != urls_.end()) { + DoStartDownload(*current_url_); + return; + } + + // Try downloading using the next downloader. + if (successor_ && !urls_.empty()) { + successor_->StartDownload(urls_, expected_hash_, + std::move(download_callback_)); + return; + } + + // The download ends here since there is no url nor downloader to handle this + // download request further. + main_task_runner()->PostTask( + FROM_HERE, base::BindOnce(std::move(download_callback_), result)); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/crx_downloader.h b/src/updater_components/update_client/crx_downloader.h new file mode 100644 index 0000000..4824e3f --- /dev/null +++ b/src/updater_components/update_client/crx_downloader.h
@@ -0,0 +1,164 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_CRX_DOWNLOADER_H_ +#define COMPONENTS_UPDATE_CLIENT_CRX_DOWNLOADER_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread_checker.h" +#include "url/gurl.h" + +namespace update_client { + +class NetworkFetcherFactory; + +// Defines a download interface for downloading components, with retrying on +// fallback urls in case of errors. This class implements a chain of +// responsibility design pattern. It can give successors in the chain a chance +// to handle a download request, until one of them succeeds, or there are no +// more urls or successors to try. A callback is always called at the end of +// the download, one time only. +// When multiple urls and downloaders exists, first all the urls are tried, in +// the order they are provided in the StartDownload function argument. After +// that, the download request is routed to the next downloader in the chain. +// The members of this class expect to be called from the main thread only. +class CrxDownloader { + public: + struct DownloadMetrics { + enum Downloader { kNone = 0, kUrlFetcher, kBits }; + + DownloadMetrics(); + + GURL url; + + Downloader downloader; + + int error; + + int64_t downloaded_bytes; // -1 means that the byte count is unknown. + int64_t total_bytes; + + uint64_t download_time_ms; + }; + + // Contains the progress or the outcome of the download. + struct Result { + // Download error: 0 indicates success. + int error = 0; + + // Path of the downloaded file if the download was successful. + base::FilePath response; + }; + + // The callback fires only once, regardless of how many urls are tried, and + // how many successors in the chain of downloaders have handled the + // download. The callback interface can be extended if needed to provide + // more visibility into how the download has been handled, including + // specific error codes and download metrics. + using DownloadCallback = base::OnceCallback<void(const Result& result)>; + + // The callback may fire 0 or once during a download. Since this + // class implements a chain of responsibility, the callback can fire for + // different urls and different downloaders. + using ProgressCallback = base::RepeatingCallback<void()>; + + using Factory = + std::unique_ptr<CrxDownloader> (*)(bool, + scoped_refptr<NetworkFetcherFactory>); + + // Factory method to create an instance of this class and build the + // chain of responsibility. |is_background_download| specifies that a + // background downloader be used, if the platform supports it. + // |task_runner| should be a task runner able to run blocking + // code such as file IO operations. + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory); + virtual ~CrxDownloader(); + + void set_progress_callback(const ProgressCallback& progress_callback); + + // Starts the download. One instance of the class handles one download only. + // One instance of CrxDownloader can only be started once, otherwise the + // behavior is undefined. The callback gets invoked if the download can't + // be started. |expected_hash| represents the SHA256 cryptographic hash of + // the download payload, represented as a hexadecimal string. + void StartDownloadFromUrl(const GURL& url, + const std::string& expected_hash, + DownloadCallback download_callback); + void StartDownload(const std::vector<GURL>& urls, + const std::string& expected_hash, + DownloadCallback download_callback); + + const std::vector<DownloadMetrics> download_metrics() const; + + protected: + explicit CrxDownloader(std::unique_ptr<CrxDownloader> successor); + + // Handles the fallback in the case of multiple urls and routing of the + // download to the following successor in the chain. Derived classes must call + // this function after each attempt at downloading the urls provided + // in the StartDownload function. + // In case of errors, |is_handled| indicates that a server side error has + // occured for the current url and the url should not be retried down + // the chain to avoid DDOS of the server. This url will be removed from the + // list of url and never tried again. + void OnDownloadComplete(bool is_handled, + const Result& result, + const DownloadMetrics& download_metrics); + + // Calls the callback when progress is made. + void OnDownloadProgress(); + + // Returns the url which is currently being downloaded from. + GURL url() const; + + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner() const { + return main_task_runner_; + } + + private: + virtual void DoStartDownload(const GURL& url) = 0; + + void VerifyResponse(bool is_handled, + Result result, + DownloadMetrics download_metrics); + + void HandleDownloadError(bool is_handled, + const Result& result, + const DownloadMetrics& download_metrics); + + base::ThreadChecker thread_checker_; + + // Used to post callbacks to the main thread. + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; + + std::vector<GURL> urls_; + + // The SHA256 hash of the download payload in hexadecimal format. + std::string expected_hash_; + std::unique_ptr<CrxDownloader> successor_; + DownloadCallback download_callback_; + ProgressCallback progress_callback_; + + std::vector<GURL>::iterator current_url_; + + std::vector<DownloadMetrics> download_metrics_; + + DISALLOW_COPY_AND_ASSIGN(CrxDownloader); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_CRX_DOWNLOADER_H_
diff --git a/src/updater_components/update_client/crx_downloader_unittest.cc b/src/updater_components/update_client/crx_downloader_unittest.cc new file mode 100644 index 0000000..db3611e --- /dev/null +++ b/src/updater_components/update_client/crx_downloader_unittest.cc
@@ -0,0 +1,417 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/crx_downloader.h" + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/memory/ref_counted.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/test/bind_test_util.h" +#include "base/test/task_environment.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "components/update_client/net/network_chromium.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/utils.h" +#include "net/base/net_errors.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::ContentsEqual; + +namespace update_client { + +namespace { + +const char kTestFileName[] = "jebgalgnebhfojomionfpkfelancnnkf.crx"; + +const char hash_jebg[] = + "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87"; + +base::FilePath MakeTestFilePath(const char* file) { + base::FilePath path; + base::PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("components/test/data/update_client") + .AppendASCII(file); +} + +} // namespace + +class CrxDownloaderTest : public testing::Test { + public: + CrxDownloaderTest(); + ~CrxDownloaderTest() override; + + // Overrides from testing::Test. + void SetUp() override; + void TearDown() override; + + void Quit(); + void RunThreads(); + void RunThreadsUntilIdle(); + + void DownloadComplete(int crx_context, const CrxDownloader::Result& result); + + void DownloadProgress(int crx_context); + + int GetInterceptorCount() { return interceptor_count_; } + + void AddResponse(const GURL& url, + const base::FilePath& file_path, + int net_error); + + protected: + std::unique_ptr<CrxDownloader> crx_downloader_; + + network::TestURLLoaderFactory test_url_loader_factory_; + + CrxDownloader::DownloadCallback callback_; + CrxDownloader::ProgressCallback progress_callback_; + + int crx_context_; + + int num_download_complete_calls_; + CrxDownloader::Result download_complete_result_; + + // These members are updated by DownloadProgress. + int num_progress_calls_; + + // Accumulates the number of loads triggered. + int interceptor_count_ = 0; + + // A magic value for the context to be used in the tests. + static const int kExpectedContext = 0xaabb; + + private: + base::test::TaskEnvironment task_environment_; + scoped_refptr<network::SharedURLLoaderFactory> + test_shared_url_loader_factory_; + base::OnceClosure quit_closure_; +}; + +const int CrxDownloaderTest::kExpectedContext; + +CrxDownloaderTest::CrxDownloaderTest() + : callback_(base::BindOnce(&CrxDownloaderTest::DownloadComplete, + base::Unretained(this), + kExpectedContext)), + progress_callback_(base::Bind(&CrxDownloaderTest::DownloadProgress, + base::Unretained(this), + kExpectedContext)), + crx_context_(0), + num_download_complete_calls_(0), + num_progress_calls_(0), + task_environment_(base::test::TaskEnvironment::MainThreadType::IO), + test_shared_url_loader_factory_( + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( + &test_url_loader_factory_)) {} + +CrxDownloaderTest::~CrxDownloaderTest() {} + +void CrxDownloaderTest::SetUp() { + num_download_complete_calls_ = 0; + download_complete_result_ = CrxDownloader::Result(); + num_progress_calls_ = 0; + + // Do not use the background downloader in these tests. + crx_downloader_ = CrxDownloader::Create( + false, base::MakeRefCounted<NetworkFetcherChromiumFactory>( + test_shared_url_loader_factory_)); + crx_downloader_->set_progress_callback(progress_callback_); + + test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting( + [&](const network::ResourceRequest& request) { interceptor_count_++; })); +} + +void CrxDownloaderTest::TearDown() { + crx_downloader_.reset(); +} + +void CrxDownloaderTest::Quit() { + if (!quit_closure_.is_null()) + std::move(quit_closure_).Run(); +} + +void CrxDownloaderTest::DownloadComplete(int crx_context, + const CrxDownloader::Result& result) { + ++num_download_complete_calls_; + crx_context_ = crx_context; + download_complete_result_ = result; + Quit(); +} + +void CrxDownloaderTest::DownloadProgress(int crx_context) { + ++num_progress_calls_; +} + +void CrxDownloaderTest::AddResponse(const GURL& url, + const base::FilePath& file_path, + int net_error) { + if (net_error == net::OK) { + std::string data; + EXPECT_TRUE(base::ReadFileToString(file_path, &data)); + network::ResourceResponseHead head; + head.content_length = data.size(); + network::URLLoaderCompletionStatus status(net_error); + status.decoded_body_length = data.size(); + test_url_loader_factory_.AddResponse(url, head, data, status); + return; + } + + EXPECT_NE(net_error, net::OK); + test_url_loader_factory_.AddResponse( + url, network::ResourceResponseHead(), std::string(), + network::URLLoaderCompletionStatus(net_error)); +} + +void CrxDownloaderTest::RunThreads() { + base::RunLoop runloop; + quit_closure_ = runloop.QuitClosure(); + runloop.Run(); + + // Since some tests need to drain currently enqueued tasks such as network + // intercepts on the IO thread, run the threads until they are + // idle. The component updater service won't loop again until the loop count + // is set and the service is started. + RunThreadsUntilIdle(); +} + +void CrxDownloaderTest::RunThreadsUntilIdle() { + task_environment_.RunUntilIdle(); + base::RunLoop().RunUntilIdle(); +} + +// Tests that starting a download without a url results in an error. +TEST_F(CrxDownloaderTest, NoUrl) { + std::vector<GURL> urls; + crx_downloader_->StartDownload(urls, std::string("abcd"), + std::move(callback_)); + RunThreadsUntilIdle(); + + EXPECT_EQ(1, num_download_complete_calls_); + EXPECT_EQ(kExpectedContext, crx_context_); + EXPECT_EQ(static_cast<int>(CrxDownloaderError::NO_URL), + download_complete_result_.error); + EXPECT_TRUE(download_complete_result_.response.empty()); + EXPECT_EQ(0, num_progress_calls_); +} + +// Tests that starting a download without providing a hash results in an error. +TEST_F(CrxDownloaderTest, NoHash) { + std::vector<GURL> urls(1, GURL("http://somehost/somefile")); + + crx_downloader_->StartDownload(urls, std::string(), std::move(callback_)); + RunThreadsUntilIdle(); + + EXPECT_EQ(1, num_download_complete_calls_); + EXPECT_EQ(kExpectedContext, crx_context_); + EXPECT_EQ(static_cast<int>(CrxDownloaderError::NO_HASH), + download_complete_result_.error); + EXPECT_TRUE(download_complete_result_.response.empty()); + EXPECT_EQ(0, num_progress_calls_); +} + +// Tests that downloading from one url is successful. +TEST_F(CrxDownloaderTest, OneUrl) { + const GURL expected_crx_url = + GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx"); + + const base::FilePath test_file(MakeTestFilePath(kTestFileName)); + AddResponse(expected_crx_url, test_file, net::OK); + + crx_downloader_->StartDownloadFromUrl( + expected_crx_url, std::string(hash_jebg), std::move(callback_)); + RunThreads(); + + EXPECT_EQ(1, GetInterceptorCount()); + + EXPECT_EQ(1, num_download_complete_calls_); + EXPECT_EQ(kExpectedContext, crx_context_); + EXPECT_EQ(0, download_complete_result_.error); + EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file)); + + EXPECT_TRUE( + DeleteFileAndEmptyParentDirectory(download_complete_result_.response)); + + EXPECT_LE(1, num_progress_calls_); +} + +// Tests that downloading from one url fails if the actual hash of the file +// does not match the expected hash. +TEST_F(CrxDownloaderTest, OneUrlBadHash) { + const GURL expected_crx_url = + GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx"); + + const base::FilePath test_file(MakeTestFilePath(kTestFileName)); + AddResponse(expected_crx_url, test_file, net::OK); + + crx_downloader_->StartDownloadFromUrl( + expected_crx_url, + std::string( + "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9"), + std::move(callback_)); + RunThreads(); + + EXPECT_EQ(1, GetInterceptorCount()); + + EXPECT_EQ(1, num_download_complete_calls_); + EXPECT_EQ(kExpectedContext, crx_context_); + EXPECT_EQ(static_cast<int>(CrxDownloaderError::BAD_HASH), + download_complete_result_.error); + EXPECT_TRUE(download_complete_result_.response.empty()); + + EXPECT_LE(1, num_progress_calls_); +} + +// Tests that specifying two urls has no side effects. Expect a successful +// download, and only one download request be made. +TEST_F(CrxDownloaderTest, TwoUrls) { + const GURL expected_crx_url = + GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx"); + + const base::FilePath test_file(MakeTestFilePath(kTestFileName)); + AddResponse(expected_crx_url, test_file, net::OK); + + std::vector<GURL> urls; + urls.push_back(expected_crx_url); + urls.push_back(expected_crx_url); + + crx_downloader_->StartDownload(urls, std::string(hash_jebg), + std::move(callback_)); + RunThreads(); + + EXPECT_EQ(1, GetInterceptorCount()); + + EXPECT_EQ(1, num_download_complete_calls_); + EXPECT_EQ(kExpectedContext, crx_context_); + EXPECT_EQ(0, download_complete_result_.error); + EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file)); + + EXPECT_TRUE( + DeleteFileAndEmptyParentDirectory(download_complete_result_.response)); + + EXPECT_LE(1, num_progress_calls_); +} + +// Tests that the fallback to a valid url is successful. +TEST_F(CrxDownloaderTest, TwoUrls_FirstInvalid) { + const GURL expected_crx_url = + GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx"); + const GURL no_file_url = + GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc.crx"); + + const base::FilePath test_file(MakeTestFilePath(kTestFileName)); + AddResponse(expected_crx_url, test_file, net::OK); + AddResponse(no_file_url, base::FilePath(), net::ERR_FILE_NOT_FOUND); + + std::vector<GURL> urls; + urls.push_back(no_file_url); + urls.push_back(expected_crx_url); + + crx_downloader_->StartDownload(urls, std::string(hash_jebg), + std::move(callback_)); + RunThreads(); + + EXPECT_EQ(2, GetInterceptorCount()); + + EXPECT_EQ(1, num_download_complete_calls_); + EXPECT_EQ(kExpectedContext, crx_context_); + EXPECT_EQ(0, download_complete_result_.error); + EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file)); + + EXPECT_TRUE( + DeleteFileAndEmptyParentDirectory(download_complete_result_.response)); + + // Expect at least some progress reported by the loader. + EXPECT_LE(1, num_progress_calls_); + + const auto download_metrics = crx_downloader_->download_metrics(); + ASSERT_EQ(2u, download_metrics.size()); + EXPECT_EQ(no_file_url, download_metrics[0].url); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, download_metrics[0].error); + EXPECT_EQ(-1, download_metrics[0].downloaded_bytes); + EXPECT_EQ(-1, download_metrics[0].total_bytes); + EXPECT_EQ(expected_crx_url, download_metrics[1].url); + EXPECT_EQ(0, download_metrics[1].error); + EXPECT_EQ(1843, download_metrics[1].downloaded_bytes); + EXPECT_EQ(1843, download_metrics[1].total_bytes); +} + +// Tests that the download succeeds if the first url is correct and the +// second bad url does not have a side-effect. +TEST_F(CrxDownloaderTest, TwoUrls_SecondInvalid) { + const GURL expected_crx_url = + GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx"); + const GURL no_file_url = + GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc.crx"); + + const base::FilePath test_file(MakeTestFilePath(kTestFileName)); + AddResponse(expected_crx_url, test_file, net::OK); + AddResponse(no_file_url, base::FilePath(), net::ERR_FILE_NOT_FOUND); + + std::vector<GURL> urls; + urls.push_back(expected_crx_url); + urls.push_back(no_file_url); + + crx_downloader_->StartDownload(urls, std::string(hash_jebg), + std::move(callback_)); + RunThreads(); + + EXPECT_EQ(1, GetInterceptorCount()); + + EXPECT_EQ(1, num_download_complete_calls_); + EXPECT_EQ(kExpectedContext, crx_context_); + EXPECT_EQ(0, download_complete_result_.error); + EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file)); + + EXPECT_TRUE( + DeleteFileAndEmptyParentDirectory(download_complete_result_.response)); + + EXPECT_LE(1, num_progress_calls_); + + EXPECT_EQ(1u, crx_downloader_->download_metrics().size()); +} + +// Tests that the download fails if both urls don't serve content. +TEST_F(CrxDownloaderTest, TwoUrls_BothInvalid) { + const GURL expected_crx_url = + GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx"); + + AddResponse(expected_crx_url, base::FilePath(), net::ERR_FILE_NOT_FOUND); + + std::vector<GURL> urls; + urls.push_back(expected_crx_url); + urls.push_back(expected_crx_url); + + crx_downloader_->StartDownload(urls, std::string(hash_jebg), + std::move(callback_)); + RunThreads(); + + EXPECT_EQ(2, GetInterceptorCount()); + + EXPECT_EQ(1, num_download_complete_calls_); + EXPECT_EQ(kExpectedContext, crx_context_); + EXPECT_NE(0, download_complete_result_.error); + EXPECT_TRUE(download_complete_result_.response.empty()); + + const auto download_metrics = crx_downloader_->download_metrics(); + ASSERT_EQ(2u, download_metrics.size()); + EXPECT_EQ(expected_crx_url, download_metrics[0].url); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, download_metrics[0].error); + EXPECT_EQ(-1, download_metrics[0].downloaded_bytes); + EXPECT_EQ(-1, download_metrics[0].total_bytes); + EXPECT_EQ(expected_crx_url, download_metrics[1].url); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, download_metrics[1].error); + EXPECT_EQ(-1, download_metrics[1].downloaded_bytes); + EXPECT_EQ(-1, download_metrics[1].total_bytes); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/crx_update_item.h b/src/updater_components/update_client/crx_update_item.h new file mode 100644 index 0000000..ac645b5 --- /dev/null +++ b/src/updater_components/update_client/crx_update_item.h
@@ -0,0 +1,51 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_CRX_UPDATE_ITEM_H_ +#define COMPONENTS_UPDATE_CLIENT_CRX_UPDATE_ITEM_H_ + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "base/time/time.h" +#include "base/version.h" +#include "components/update_client/crx_downloader.h" +#include "components/update_client/update_client.h" +#include "components/update_client/update_client_errors.h" + +namespace update_client { + +struct CrxUpdateItem { + CrxUpdateItem(); + CrxUpdateItem(const CrxUpdateItem& other); + ~CrxUpdateItem(); + + ComponentState state; + + std::string id; + + // The value of this data member is provided to the |UpdateClient| by the + // caller by responding to the |CrxDataCallback|. If the caller can't + // provide this value, for instance, in cases where the CRX was uninstalled, + // then the |component| member will not be present. + base::Optional<CrxComponent> component; + + // Time when an update check for this CRX has happened. + base::TimeTicks last_check; + + base::Version next_version; + std::string next_fp; + + ErrorCategory error_category = ErrorCategory::kNone; + int error_code = 0; + int extra_code1 = 0; +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_CRX_UPDATE_ITEM_H_
diff --git a/src/updater_components/update_client/net/network_chromium.h b/src/updater_components/update_client/net/network_chromium.h new file mode 100644 index 0000000..e8ac9bb --- /dev/null +++ b/src/updater_components/update_client/net/network_chromium.h
@@ -0,0 +1,39 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_NET_NETWORK_CHROMIUM_H_ +#define COMPONENTS_UPDATE_CLIENT_NET_NETWORK_CHROMIUM_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/update_client/network.h" + +namespace network { +class SharedURLLoaderFactory; +} + +namespace update_client { + +class NetworkFetcherChromiumFactory : public NetworkFetcherFactory { + public: + explicit NetworkFetcherChromiumFactory( + scoped_refptr<network::SharedURLLoaderFactory> + shared_url_network_factory); + + std::unique_ptr<NetworkFetcher> Create() const override; + + protected: + ~NetworkFetcherChromiumFactory() override; + + private: + scoped_refptr<network::SharedURLLoaderFactory> shared_url_network_factory_; + + DISALLOW_COPY_AND_ASSIGN(NetworkFetcherChromiumFactory); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_NET_NETWORK_CHROMIUM_H_
diff --git a/src/updater_components/update_client/net/network_impl.cc b/src/updater_components/update_client/net/network_impl.cc new file mode 100644 index 0000000..1e241a6 --- /dev/null +++ b/src/updater_components/update_client/net/network_impl.cc
@@ -0,0 +1,205 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/net/network_impl.h" + +#include <utility> + +#include "base/bind.h" +#include "base/numerics/safe_conversions.h" +#include "components/update_client/net/network_chromium.h" +#include "net/base/load_flags.h" +#include "net/http/http_response_headers.h" +#include "net/traffic_annotation/network_traffic_annotation.h" +#include "services/network/public/cpp/resource_response.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "url/gurl.h" + +namespace { + +const net::NetworkTrafficAnnotationTag traffic_annotation = + net::DefineNetworkTrafficAnnotation("update_client", R"( + semantics { + sender: "Component Updater and Extension Updater" + description: + "This network module is used by both the component and the " + "extension updaters in Chrome. " + "The component updater is responsible for updating code and data " + "modules such as Flash, CrlSet, Origin Trials, etc. These modules " + "are updated on cycles independent of the Chrome release tracks. " + "It runs in the browser process and communicates with a set of " + "servers using the Omaha protocol to find the latest versions of " + "components, download them, and register them with the rest of " + "Chrome. " + "The extension updater works similarly, but it updates user " + "extensions instead of Chrome components. " + trigger: "Manual or automatic software updates." + data: + "Various OS and Chrome parameters such as version, bitness, " + "release tracks, etc. The component and the extension ids are also " + "present in both the request and the response from the servers. " + "The URL that refers to a component CRX payload is obfuscated for " + "most components." + destination: GOOGLE_OWNED_SERVICE + } + policy { + cookies_allowed: NO + setting: "This feature cannot be disabled." + chrome_policy { + ComponentUpdatesEnabled { + policy_options {mode: MANDATORY} + ComponentUpdatesEnabled: false + } + } + })"); + +// Returns the string value of a header of the server response or an empty +// string if the header is not available. Only the first header is returned +// if multiple instances of the same header are present. +std::string GetStringHeader(const network::SimpleURLLoader* simple_url_loader, + const char* header_name) { + DCHECK(simple_url_loader); + + const auto* response_info = simple_url_loader->ResponseInfo(); + if (!response_info || !response_info->headers) + return {}; + + std::string header_value; + return response_info->headers->EnumerateHeader(nullptr, header_name, + &header_value) + ? header_value + : std::string{}; +} + +// Returns the integral value of a header of the server response or -1 if +// if the header is not available or a conversion error has occured. +int64_t GetInt64Header(const network::SimpleURLLoader* simple_url_loader, + const char* header_name) { + DCHECK(simple_url_loader); + + const auto* response_info = simple_url_loader->ResponseInfo(); + if (!response_info || !response_info->headers) + return -1; + + return response_info->headers->GetInt64HeaderValue(header_name); +} + +} // namespace + +namespace update_client { + +NetworkFetcherImpl::NetworkFetcherImpl( + scoped_refptr<network::SharedURLLoaderFactory> shared_url_network_factory) + : shared_url_network_factory_(shared_url_network_factory) {} +NetworkFetcherImpl::~NetworkFetcherImpl() = default; + +void NetworkFetcherImpl::PostRequest( + const GURL& url, + const std::string& post_data, + const base::flat_map<std::string, std::string>& post_additional_headers, + ResponseStartedCallback response_started_callback, + ProgressCallback progress_callback, + PostRequestCompleteCallback post_request_complete_callback) { + DCHECK(!simple_url_loader_); + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->url = url; + resource_request->method = "POST"; + resource_request->load_flags = net::LOAD_DISABLE_CACHE; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; + for (const auto& header : post_additional_headers) + resource_request->headers.SetHeader(header.first, header.second); + simple_url_loader_ = network::SimpleURLLoader::Create( + std::move(resource_request), traffic_annotation); + simple_url_loader_->SetRetryOptions( + kMaxRetriesOnNetworkChange, + network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE); + simple_url_loader_->AttachStringForUpload(post_data, "application/json"); + simple_url_loader_->SetOnResponseStartedCallback(base::BindOnce( + &NetworkFetcherImpl::OnResponseStartedCallback, base::Unretained(this), + std::move(response_started_callback))); + simple_url_loader_->SetOnDownloadProgressCallback(base::BindRepeating( + &NetworkFetcherImpl::OnProgressCallback, base::Unretained(this), + std::move(progress_callback))); + constexpr size_t kMaxResponseSize = 1024 * 1024; + simple_url_loader_->DownloadToString( + shared_url_network_factory_.get(), + base::BindOnce( + [](const network::SimpleURLLoader* simple_url_loader, + PostRequestCompleteCallback post_request_complete_callback, + std::unique_ptr<std::string> response_body) { + std::move(post_request_complete_callback) + .Run(std::move(response_body), simple_url_loader->NetError(), + GetStringHeader(simple_url_loader, kHeaderEtag), + GetInt64Header(simple_url_loader, kHeaderXRetryAfter)); + }, + simple_url_loader_.get(), std::move(post_request_complete_callback)), + kMaxResponseSize); +} + +void NetworkFetcherImpl::DownloadToFile( + const GURL& url, + const base::FilePath& file_path, + ResponseStartedCallback response_started_callback, + ProgressCallback progress_callback, + DownloadToFileCompleteCallback download_to_file_complete_callback) { + DCHECK(!simple_url_loader_); + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->url = url; + resource_request->method = "GET"; + resource_request->load_flags = net::LOAD_DISABLE_CACHE; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; + simple_url_loader_ = network::SimpleURLLoader::Create( + std::move(resource_request), traffic_annotation); + simple_url_loader_->SetRetryOptions( + kMaxRetriesOnNetworkChange, + network::SimpleURLLoader::RetryMode::RETRY_ON_NETWORK_CHANGE); + simple_url_loader_->SetAllowPartialResults(true); + simple_url_loader_->SetOnResponseStartedCallback(base::BindOnce( + &NetworkFetcherImpl::OnResponseStartedCallback, base::Unretained(this), + std::move(response_started_callback))); + simple_url_loader_->SetOnDownloadProgressCallback(base::BindRepeating( + &NetworkFetcherImpl::OnProgressCallback, base::Unretained(this), + std::move(progress_callback))); + simple_url_loader_->DownloadToFile( + shared_url_network_factory_.get(), + base::BindOnce( + [](const network::SimpleURLLoader* simple_url_loader, + DownloadToFileCompleteCallback download_to_file_complete_callback, + base::FilePath file_path) { + std::move(download_to_file_complete_callback) + .Run(file_path, simple_url_loader->NetError(), + simple_url_loader->GetContentSize()); + }, + simple_url_loader_.get(), + std::move(download_to_file_complete_callback)), + file_path); +} + +void NetworkFetcherImpl::OnResponseStartedCallback( + ResponseStartedCallback response_started_callback, + const GURL& final_url, + const network::ResourceResponseHead& response_head) { + std::move(response_started_callback) + .Run(final_url, + response_head.headers ? response_head.headers->response_code() : -1, + response_head.content_length); +} + +void NetworkFetcherImpl::OnProgressCallback(ProgressCallback progress_callback, + uint64_t current) { + progress_callback.Run(base::saturated_cast<int64_t>(current)); +} + +NetworkFetcherChromiumFactory::NetworkFetcherChromiumFactory( + scoped_refptr<network::SharedURLLoaderFactory> shared_url_network_factory) + : shared_url_network_factory_(shared_url_network_factory) {} + +NetworkFetcherChromiumFactory::~NetworkFetcherChromiumFactory() = default; + +std::unique_ptr<NetworkFetcher> NetworkFetcherChromiumFactory::Create() const { + return std::make_unique<NetworkFetcherImpl>(shared_url_network_factory_); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/net/network_impl.h b/src/updater_components/update_client/net/network_impl.h new file mode 100644 index 0000000..f28cf2f --- /dev/null +++ b/src/updater_components/update_client/net/network_impl.h
@@ -0,0 +1,65 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_NET_NETWORK_IMPL_H_ +#define COMPONENTS_UPDATE_CLIENT_NET_NETWORK_IMPL_H_ + +#include <stdint.h> + +#include <memory> +#include <string> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/update_client/network.h" + +namespace network { +struct ResourceResponseHead; +class SharedURLLoaderFactory; +class SimpleURLLoader; +} // namespace network + +namespace update_client { + +class NetworkFetcherImpl : public NetworkFetcher { + public: + explicit NetworkFetcherImpl(scoped_refptr<network::SharedURLLoaderFactory> + shared_url_network_factory); + ~NetworkFetcherImpl() override; + + // NetworkFetcher overrides. + void PostRequest( + const GURL& url, + const std::string& post_data, + const base::flat_map<std::string, std::string>& post_additional_headers, + ResponseStartedCallback response_started_callback, + ProgressCallback progress_callback, + PostRequestCompleteCallback post_request_complete_callback) override; + void DownloadToFile(const GURL& url, + const base::FilePath& file_path, + ResponseStartedCallback response_started_callback, + ProgressCallback progress_callback, + DownloadToFileCompleteCallback + download_to_file_complete_callback) override; + + private: + void OnResponseStartedCallback( + ResponseStartedCallback response_started_callback, + const GURL& final_url, + const network::ResourceResponseHead& response_head); + + void OnProgressCallback(ProgressCallback response_started_callback, + uint64_t current); + + static constexpr int kMaxRetriesOnNetworkChange = 3; + + scoped_refptr<network::SharedURLLoaderFactory> shared_url_network_factory_; + std::unique_ptr<network::SimpleURLLoader> simple_url_loader_; + + DISALLOW_COPY_AND_ASSIGN(NetworkFetcherImpl); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_NET_NETWORK_IMPL_H_
diff --git a/src/updater_components/update_client/net/url_loader_post_interceptor.cc b/src/updater_components/update_client/net/url_loader_post_interceptor.cc new file mode 100644 index 0000000..11a2af8 --- /dev/null +++ b/src/updater_components/update_client/net/url_loader_post_interceptor.cc
@@ -0,0 +1,260 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/net/url_loader_post_interceptor.h" + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "base/test/bind_test_util.h" +#include "components/update_client/test_configurator.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/test/test_url_loader_factory.h" +#include "services/network/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace update_client { + +URLLoaderPostInterceptor::URLLoaderPostInterceptor( + network::TestURLLoaderFactory* url_loader_factory) + : url_loader_factory_(url_loader_factory) { + filtered_urls_.push_back( + GURL(base::StringPrintf("%s://%s%s", POST_INTERCEPT_SCHEME, + POST_INTERCEPT_HOSTNAME, POST_INTERCEPT_PATH))); + InitializeWithInterceptor(); +} + +URLLoaderPostInterceptor::URLLoaderPostInterceptor( + std::vector<GURL> supported_urls, + network::TestURLLoaderFactory* url_loader_factory) + : url_loader_factory_(url_loader_factory) { + DCHECK_LT(0u, supported_urls.size()); + filtered_urls_.swap(supported_urls); + InitializeWithInterceptor(); +} + +URLLoaderPostInterceptor::URLLoaderPostInterceptor( + std::vector<GURL> supported_urls, + net::test_server::EmbeddedTestServer* embedded_test_server) + : embedded_test_server_(embedded_test_server) { + DCHECK_LT(0u, supported_urls.size()); + filtered_urls_.swap(supported_urls); + InitializeWithRequestHandler(); +} + +URLLoaderPostInterceptor::~URLLoaderPostInterceptor() {} + +bool URLLoaderPostInterceptor::ExpectRequest( + std::unique_ptr<RequestMatcher> request_matcher) { + return ExpectRequest(std::move(request_matcher), net::HTTP_OK); +} + +bool URLLoaderPostInterceptor::ExpectRequest( + std::unique_ptr<RequestMatcher> request_matcher, + net::HttpStatusCode response_code) { + expectations_.push( + {std::move(request_matcher), ExpectationResponse(response_code, "")}); + return true; +} + +bool URLLoaderPostInterceptor::ExpectRequest( + std::unique_ptr<RequestMatcher> request_matcher, + const base::FilePath& filepath) { + std::string response; + if (filepath.empty() || !base::ReadFileToString(filepath, &response)) + return false; + + expectations_.push({std::move(request_matcher), + ExpectationResponse(net::HTTP_OK, response)}); + return true; +} + +// Returns how many requests have been intercepted and matched by +// an expectation. One expectation can only be matched by one request. +int URLLoaderPostInterceptor::GetHitCount() const { + return hit_count_; +} + +// Returns how many requests in total have been captured by the interceptor. +int URLLoaderPostInterceptor::GetCount() const { + return static_cast<int>(requests_.size()); +} + +// Returns all requests that have been intercepted, matched or not. +std::vector<URLLoaderPostInterceptor::InterceptedRequest> +URLLoaderPostInterceptor::GetRequests() const { + return requests_; +} + +// Return the body of the n-th request, zero-based. +std::string URLLoaderPostInterceptor::GetRequestBody(size_t n) const { + return std::get<0>(requests_[n]); +} + +// Returns the joined bodies of all requests for debugging purposes. +std::string URLLoaderPostInterceptor::GetRequestsAsString() const { + const std::vector<InterceptedRequest> requests = GetRequests(); + std::string s = "Requests are:"; + int i = 0; + for (auto it = requests.cbegin(); it != requests.cend(); ++it) + s.append(base::StringPrintf("\n [%d]: %s", ++i, std::get<0>(*it).c_str())); + return s; +} + +// Resets the state of the interceptor so that new expectations can be set. +void URLLoaderPostInterceptor::Reset() { + hit_count_ = 0; + requests_.clear(); + base::queue<Expectation>().swap(expectations_); +} + +void URLLoaderPostInterceptor::Pause() { + is_paused_ = true; +} + +void URLLoaderPostInterceptor::Resume() { + is_paused_ = false; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { + if (!pending_expectations_.size()) + return; + + PendingExpectation expectation = + std::move(pending_expectations_.front()); + pending_expectations_.pop(); + url_loader_factory_->AddResponse(expectation.first.spec(), + expectation.second.response_body, + expectation.second.response_code); + })); +} + +void URLLoaderPostInterceptor::url_job_request_ready_callback( + UrlJobRequestReadyCallback url_job_request_ready_callback) { + url_job_request_ready_callback_ = std::move(url_job_request_ready_callback); +} + +int URLLoaderPostInterceptor::GetHitCountForURL(const GURL& url) { + int hit_count = 0; + const std::vector<InterceptedRequest> requests = GetRequests(); + for (auto it = requests.cbegin(); it != requests.cend(); ++it) { + GURL url_no_query = std::get<2>(*it); + if (url_no_query.has_query()) { + GURL::Replacements replacements; + replacements.ClearQuery(); + url_no_query = url_no_query.ReplaceComponents(replacements); + } + if (url_no_query == url) + hit_count++; + } + return hit_count; +} + +void URLLoaderPostInterceptor::InitializeWithInterceptor() { + DCHECK(url_loader_factory_); + url_loader_factory_->SetInterceptor( + base::BindLambdaForTesting([&](const network::ResourceRequest& request) { + GURL url = request.url; + if (url.has_query()) { + GURL::Replacements replacements; + replacements.ClearQuery(); + url = url.ReplaceComponents(replacements); + } + auto it = std::find_if( + filtered_urls_.begin(), filtered_urls_.end(), + [url](const GURL& filtered_url) { return filtered_url == url; }); + if (it == filtered_urls_.end()) + return; + + std::string request_body = network::GetUploadData(request); + requests_.push_back({request_body, request.headers, request.url}); + if (expectations_.empty()) + return; + const auto& expectation = expectations_.front(); + if (expectation.first->Match(request_body)) { + const net::HttpStatusCode response_code( + expectation.second.response_code); + const std::string response_body(expectation.second.response_body); + + if (url_job_request_ready_callback_) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(url_job_request_ready_callback_)); + } + + if (!is_paused_) { + url_loader_factory_->AddResponse(request.url.spec(), response_body, + response_code); + } else { + pending_expectations_.push({request.url, expectation.second}); + } + expectations_.pop(); + ++hit_count_; + } + })); +} + +void URLLoaderPostInterceptor::InitializeWithRequestHandler() { + DCHECK(embedded_test_server_); + DCHECK(!url_loader_factory_); + embedded_test_server_->RegisterRequestHandler(base::BindRepeating( + &URLLoaderPostInterceptor::RequestHandler, base::Unretained(this))); +} + +std::unique_ptr<net::test_server::HttpResponse> +URLLoaderPostInterceptor::RequestHandler( + const net::test_server::HttpRequest& request) { + // Only intercepts POST. + if (request.method != net::test_server::METHOD_POST) + return nullptr; + + GURL url = request.GetURL(); + if (url.has_query()) { + GURL::Replacements replacements; + replacements.ClearQuery(); + url = url.ReplaceComponents(replacements); + } + auto it = std::find_if( + filtered_urls_.begin(), filtered_urls_.end(), + [url](const GURL& filtered_url) { return filtered_url == url; }); + if (it == filtered_urls_.end()) + return nullptr; + + std::string request_body = request.content; + net::HttpRequestHeaders headers; + for (auto it : request.headers) + headers.SetHeader(it.first, it.second); + requests_.push_back({request_body, headers, url}); + if (expectations_.empty()) + return nullptr; + + const auto& expectation = expectations_.front(); + if (expectation.first->Match(request_body)) { + const net::HttpStatusCode response_code(expectation.second.response_code); + const std::string response_body(expectation.second.response_body); + expectations_.pop(); + ++hit_count_; + + std::unique_ptr<net::test_server::BasicHttpResponse> http_response( + new net::test_server::BasicHttpResponse); + http_response->set_code(response_code); + http_response->set_content(response_body); + return http_response; + } + + return nullptr; +} + +bool PartialMatch::Match(const std::string& actual) const { + return actual.find(expected_) != std::string::npos; +} + +bool AnyMatch::Match(const std::string& actual) const { + return true; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/net/url_loader_post_interceptor.h b/src/updater_components/update_client/net/url_loader_post_interceptor.h new file mode 100644 index 0000000..9907f49 --- /dev/null +++ b/src/updater_components/update_client/net/url_loader_post_interceptor.h
@@ -0,0 +1,181 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_NET_URL_LOADER_POST_INTERCEPTOR_H_ +#define COMPONENTS_UPDATE_CLIENT_NET_URL_LOADER_POST_INTERCEPTOR_H_ + +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include "base/callback.h" +#include "base/containers/queue.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "net/http/http_request_headers.h" +#include "net/http/http_status_code.h" +#include "url/gurl.h" + +namespace network { +class TestURLLoaderFactory; +} + +namespace net { +namespace test_server { +class EmbeddedTestServer; +class HttpResponse; +struct HttpRequest; +} // namespace test_server +} // namespace net + +namespace update_client { + +// Intercepts requests to a file path, counts them, and captures the body of +// the requests. Optionally, for each request, it can return a canned response +// from a given file. The class maintains a queue of expectations, and returns +// one and only one response for each request that matches the expectation. +// Then, the expectation is removed from the queue. +class URLLoaderPostInterceptor { + public: + using InterceptedRequest = + std::tuple<std::string, net::HttpRequestHeaders, GURL>; + + // Called when the load associated with the url request is intercepted + // by this object:. + using UrlJobRequestReadyCallback = base::OnceCallback<void()>; + + // Allows a generic string maching interface when setting up expectations. + class RequestMatcher { + public: + virtual bool Match(const std::string& actual) const = 0; + virtual ~RequestMatcher() {} + }; + + explicit URLLoaderPostInterceptor( + network::TestURLLoaderFactory* url_loader_factory); + URLLoaderPostInterceptor(std::vector<GURL> supported_urls, + network::TestURLLoaderFactory* url_loader_factory); + URLLoaderPostInterceptor(std::vector<GURL> supported_urls, + net::test_server::EmbeddedTestServer*); + + ~URLLoaderPostInterceptor(); + + // Sets an expection for the body of the POST request and optionally, + // provides a canned response identified by a |file_path| to be returned when + // the expectation is met. If no |file_path| is provided, then an empty + // response body is served. If |response_code| is provided, then an empty + // response body with that response code is returned. + // Returns |true| if the expectation was set. + bool ExpectRequest(std::unique_ptr<RequestMatcher> request_matcher); + + bool ExpectRequest(std::unique_ptr<RequestMatcher> request_matcher, + net::HttpStatusCode response_code); + + bool ExpectRequest(std::unique_ptr<RequestMatcher> request_matcher, + const base::FilePath& filepath); + + // Returns how many requests have been intercepted and matched by + // an expectation. One expectation can only be matched by one request. + int GetHitCount() const; + + // Returns how many requests in total have been captured by the interceptor. + int GetCount() const; + + // Returns all requests that have been intercepted, matched or not. + std::vector<InterceptedRequest> GetRequests() const; + + // Return the body of the n-th request, zero-based. + std::string GetRequestBody(size_t n) const; + + // Returns the joined bodies of all requests for debugging purposes. + std::string GetRequestsAsString() const; + + // Resets the state of the interceptor so that new expectations can be set. + void Reset(); + + // Prevents the intercepted request from starting, as a way to simulate + // the effects of a very slow network. Call this function before the actual + // network request occurs. + void Pause(); + + // Allows a previously paused request to continue. + void Resume(); + + // Sets a callback to be invoked when the request job associated with + // an intercepted request is created. This allows the test execution to + // synchronize with network tasks running on the IO thread and avoid polling + // using idle run loops. A paused request can be resumed after this callback + // has been invoked. + void url_job_request_ready_callback( + UrlJobRequestReadyCallback url_job_request_ready_callback); + + int GetHitCountForURL(const GURL& url); + + private: + void InitializeWithInterceptor(); + void InitializeWithRequestHandler(); + + std::unique_ptr<net::test_server::HttpResponse> RequestHandler( + const net::test_server::HttpRequest& request); + + struct ExpectationResponse { + ExpectationResponse(net::HttpStatusCode code, const std::string& body) + : response_code(code), response_body(body) {} + const net::HttpStatusCode response_code; + const std::string response_body; + }; + using Expectation = + std::pair<std::unique_ptr<RequestMatcher>, ExpectationResponse>; + + using PendingExpectation = std::pair<GURL, ExpectationResponse>; + + // Contains the count of the request matching expectations. + int hit_count_ = 0; + + // Contains the request body and the extra headers of the intercepted + // requests. + std::vector<InterceptedRequest> requests_; + + // Contains the expectations which this interceptor tries to match. + base::queue<Expectation> expectations_; + + base::queue<PendingExpectation> pending_expectations_; + + network::TestURLLoaderFactory* url_loader_factory_ = nullptr; + net::test_server::EmbeddedTestServer* embedded_test_server_ = nullptr; + + bool is_paused_ = false; + + std::vector<GURL> filtered_urls_; + + UrlJobRequestReadyCallback url_job_request_ready_callback_; + + DISALLOW_COPY_AND_ASSIGN(URLLoaderPostInterceptor); +}; + +class PartialMatch : public URLLoaderPostInterceptor::RequestMatcher { + public: + explicit PartialMatch(const std::string& expected) : expected_(expected) {} + bool Match(const std::string& actual) const override; + + private: + const std::string expected_; + + DISALLOW_COPY_AND_ASSIGN(PartialMatch); +}; + +class AnyMatch : public URLLoaderPostInterceptor::RequestMatcher { + public: + AnyMatch() = default; + bool Match(const std::string& actual) const override; + + private: + DISALLOW_COPY_AND_ASSIGN(AnyMatch); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_NET_URL_LOADER_POST_INTERCEPTOR_H_
diff --git a/src/updater_components/update_client/network.cc b/src/updater_components/update_client/network.cc new file mode 100644 index 0000000..cb8e151 --- /dev/null +++ b/src/updater_components/update_client/network.cc
@@ -0,0 +1,12 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/network.h" + +namespace update_client { + +constexpr char NetworkFetcher::kHeaderEtag[]; +constexpr char NetworkFetcher::kHeaderXRetryAfter[]; + +} // namespace update_client
diff --git a/src/updater_components/update_client/network.h b/src/updater_components/update_client/network.h new file mode 100644 index 0000000..daed162 --- /dev/null +++ b/src/updater_components/update_client/network.h
@@ -0,0 +1,89 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_NETWORK_H_ +#define COMPONENTS_UPDATE_CLIENT_NETWORK_H_ + +#include <stdint.h> + +#include <memory> +#include <string> + +#include "base/callback_forward.h" +#include "base/containers/flat_map.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +class GURL; + +namespace base { +class FilePath; +} // namespace base + +namespace update_client { + +class NetworkFetcher { + public: + using PostRequestCompleteCallback = + base::OnceCallback<void(std::unique_ptr<std::string> response_body, + int net_error, + const std::string& header_etag, + int64_t xheader_retry_after_sec)>; + using DownloadToFileCompleteCallback = base::OnceCallback< + void(base::FilePath path, int net_error, int64_t content_size)>; + using ResponseStartedCallback = base::OnceCallback< + void(const GURL& final_url, int response_code, int64_t content_length)>; + using ProgressCallback = base::RepeatingCallback<void(int64_t current)>; + + // The ETag header carries the ECSDA signature of the POST response, if + // signing has been used. + static constexpr char kHeaderEtag[] = "ETag"; + + // The server uses the optional X-Retry-After header to indicate that the + // current request should not be attempted again. + // + // The value of the header is the number of seconds to wait before trying to + // do a subsequent update check. Only the values retrieved over HTTPS are + // trusted. + static constexpr char kHeaderXRetryAfter[] = "X-Retry-After"; + + virtual ~NetworkFetcher() = default; + + virtual void PostRequest( + const GURL& url, + const std::string& post_data, + const base::flat_map<std::string, std::string>& post_additional_headers, + ResponseStartedCallback response_started_callback, + ProgressCallback progress_callback, + PostRequestCompleteCallback post_request_complete_callback) = 0; + virtual void DownloadToFile( + const GURL& url, + const base::FilePath& file_path, + ResponseStartedCallback response_started_callback, + ProgressCallback progress_callback, + DownloadToFileCompleteCallback download_to_file_complete_callback) = 0; + + protected: + NetworkFetcher() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(NetworkFetcher); +}; + +class NetworkFetcherFactory : public base::RefCounted<NetworkFetcherFactory> { + public: + virtual std::unique_ptr<NetworkFetcher> Create() const = 0; + + protected: + friend class base::RefCounted<NetworkFetcherFactory>; + NetworkFetcherFactory() = default; + virtual ~NetworkFetcherFactory() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(NetworkFetcherFactory); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_NETWORK_H_
diff --git a/src/updater_components/update_client/patch/patch_impl.cc b/src/updater_components/update_client/patch/patch_impl.cc new file mode 100644 index 0000000..599101f --- /dev/null +++ b/src/updater_components/update_client/patch/patch_impl.cc
@@ -0,0 +1,53 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/patch/patch_impl.h" + +#include "components/services/patch/public/cpp/patch.h" +#include "components/update_client/component_patcher_operation.h" + +namespace update_client { + +namespace { + +class PatcherImpl : public Patcher { + public: + explicit PatcherImpl(PatchChromiumFactory::Callback callback) + : callback_(std::move(callback)) {} + + void PatchBsdiff(const base::FilePath& old_file, + const base::FilePath& patch_file, + const base::FilePath& destination, + PatchCompleteCallback callback) const override { + patch::Patch(callback_.Run(), update_client::kBsdiff, old_file, patch_file, + destination, std::move(callback)); + } + + void PatchCourgette(const base::FilePath& old_file, + const base::FilePath& patch_file, + const base::FilePath& destination, + PatchCompleteCallback callback) const override { + patch::Patch(callback_.Run(), update_client::kCourgette, old_file, + patch_file, destination, std::move(callback)); + } + + protected: + ~PatcherImpl() override = default; + + private: + const PatchChromiumFactory::Callback callback_; +}; + +} // namespace + +PatchChromiumFactory::PatchChromiumFactory(Callback callback) + : callback_(std::move(callback)) {} + +scoped_refptr<Patcher> PatchChromiumFactory::Create() const { + return base::MakeRefCounted<PatcherImpl>(callback_); +} + +PatchChromiumFactory::~PatchChromiumFactory() = default; + +} // namespace update_client
diff --git a/src/updater_components/update_client/patch/patch_impl.h b/src/updater_components/update_client/patch/patch_impl.h new file mode 100644 index 0000000..e76666d --- /dev/null +++ b/src/updater_components/update_client/patch/patch_impl.h
@@ -0,0 +1,38 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PATCH_PATCH_IMPL_H_ +#define COMPONENTS_UPDATE_CLIENT_PATCH_PATCH_IMPL_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/services/patch/public/mojom/file_patcher.mojom.h" +#include "components/update_client/patcher.h" +#include "mojo/public/cpp/bindings/pending_remote.h" + +namespace update_client { + +class PatchChromiumFactory : public PatcherFactory { + public: + using Callback = + base::RepeatingCallback<mojo::PendingRemote<patch::mojom::FilePatcher>()>; + explicit PatchChromiumFactory(Callback callback); + + scoped_refptr<Patcher> Create() const override; + + protected: + ~PatchChromiumFactory() override; + + private: + const Callback callback_; + + DISALLOW_COPY_AND_ASSIGN(PatchChromiumFactory); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PATCH_PATCH_IMPL_H_
diff --git a/src/updater_components/update_client/patcher.h b/src/updater_components/update_client/patcher.h new file mode 100644 index 0000000..1316d26 --- /dev/null +++ b/src/updater_components/update_client/patcher.h
@@ -0,0 +1,56 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PATCHER_H_ +#define COMPONENTS_UPDATE_CLIENT_PATCHER_H_ + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace base { +class FilePath; +} // namespace base + +namespace update_client { + +class Patcher : public base::RefCountedThreadSafe<Patcher> { + public: + using PatchCompleteCallback = base::OnceCallback<void(int result)>; + + virtual void PatchBsdiff(const base::FilePath& input_file, + const base::FilePath& patch_file, + const base::FilePath& destination, + PatchCompleteCallback callback) const = 0; + + virtual void PatchCourgette(const base::FilePath& input_file, + const base::FilePath& patch_file, + const base::FilePath& destination, + PatchCompleteCallback callback) const = 0; + + protected: + friend class base::RefCountedThreadSafe<Patcher>; + Patcher() = default; + virtual ~Patcher() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(Patcher); +}; + +class PatcherFactory : public base::RefCountedThreadSafe<PatcherFactory> { + public: + virtual scoped_refptr<Patcher> Create() const = 0; + + protected: + friend class base::RefCountedThreadSafe<PatcherFactory>; + PatcherFactory() = default; + virtual ~PatcherFactory() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(PatcherFactory); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PATCHER_H_
diff --git a/src/updater_components/update_client/persisted_data.cc b/src/updater_components/update_client/persisted_data.cc new file mode 100644 index 0000000..ea1ad1e --- /dev/null +++ b/src/updater_components/update_client/persisted_data.cc
@@ -0,0 +1,175 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/persisted_data.h" + +#include <string> +#include <vector> + +#include "base/guid.h" +#include "base/macros.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_checker.h" +#include "base/values.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/update_client/activity_data_service.h" + +const char kPersistedDataPreference[] = "updateclientdata"; + +namespace update_client { + +PersistedData::PersistedData(PrefService* pref_service, + ActivityDataService* activity_data_service) + : pref_service_(pref_service), + activity_data_service_(activity_data_service) {} + +PersistedData::~PersistedData() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +int PersistedData::GetInt(const std::string& id, + const std::string& key, + int fallback) const { + DCHECK(thread_checker_.CalledOnValidThread()); + // We assume ids do not contain '.' characters. + DCHECK_EQ(std::string::npos, id.find('.')); + if (!pref_service_) + return fallback; + const base::DictionaryValue* dict = + pref_service_->GetDictionary(kPersistedDataPreference); + if (!dict) + return fallback; + int result = 0; + return dict->GetInteger( + base::StringPrintf("apps.%s.%s", id.c_str(), key.c_str()), &result) + ? result + : fallback; +} + +std::string PersistedData::GetString(const std::string& id, + const std::string& key) const { + DCHECK(thread_checker_.CalledOnValidThread()); + // We assume ids do not contain '.' characters. + DCHECK_EQ(std::string::npos, id.find('.')); + if (!pref_service_) + return std::string(); + const base::DictionaryValue* dict = + pref_service_->GetDictionary(kPersistedDataPreference); + if (!dict) + return std::string(); + std::string result; + return dict->GetString( + base::StringPrintf("apps.%s.%s", id.c_str(), key.c_str()), &result) + ? result + : std::string(); +} + +int PersistedData::GetDateLastRollCall(const std::string& id) const { + return GetInt(id, "dlrc", kDateUnknown); +} + +int PersistedData::GetDateLastActive(const std::string& id) const { + return GetInt(id, "dla", kDateUnknown); +} + +std::string PersistedData::GetPingFreshness(const std::string& id) const { + std::string result = GetString(id, "pf"); + return !result.empty() ? base::StringPrintf("{%s}", result.c_str()) : result; +} + +std::string PersistedData::GetCohort(const std::string& id) const { + return GetString(id, "cohort"); +} + +std::string PersistedData::GetCohortName(const std::string& id) const { + return GetString(id, "cohortname"); +} + +std::string PersistedData::GetCohortHint(const std::string& id) const { + return GetString(id, "cohorthint"); +} + +void PersistedData::SetDateLastRollCall(const std::vector<std::string>& ids, + int datenum) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!pref_service_ || datenum < 0) + return; + DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference); + for (const auto& id : ids) { + // We assume ids do not contain '.' characters. + DCHECK_EQ(std::string::npos, id.find('.')); + update->SetInteger(base::StringPrintf("apps.%s.dlrc", id.c_str()), datenum); + update->SetString(base::StringPrintf("apps.%s.pf", id.c_str()), + base::GenerateGUID()); + } +} + +void PersistedData::SetDateLastActive(const std::vector<std::string>& ids, + int datenum) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!pref_service_ || datenum < 0) + return; + DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference); + for (const auto& id : ids) { + if (GetActiveBit(id)) { + // We assume ids do not contain '.' characters. + DCHECK_EQ(std::string::npos, id.find('.')); + update->SetInteger(base::StringPrintf("apps.%s.dla", id.c_str()), + datenum); + activity_data_service_->ClearActiveBit(id); + } + } +} + +void PersistedData::SetString(const std::string& id, + const std::string& key, + const std::string& value) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!pref_service_) + return; + DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference); + update->SetString(base::StringPrintf("apps.%s.%s", id.c_str(), key.c_str()), + value); +} + +void PersistedData::SetCohort(const std::string& id, + const std::string& cohort) { + SetString(id, "cohort", cohort); +} + +void PersistedData::SetCohortName(const std::string& id, + const std::string& cohort_name) { + SetString(id, "cohortname", cohort_name); +} + +void PersistedData::SetCohortHint(const std::string& id, + const std::string& cohort_hint) { + SetString(id, "cohorthint", cohort_hint); +} + +bool PersistedData::GetActiveBit(const std::string& id) const { + return activity_data_service_ && activity_data_service_->GetActiveBit(id); +} + +int PersistedData::GetDaysSinceLastRollCall(const std::string& id) const { + DCHECK(thread_checker_.CalledOnValidThread()); + return activity_data_service_ + ? activity_data_service_->GetDaysSinceLastRollCall(id) + : kDaysUnknown; +} + +int PersistedData::GetDaysSinceLastActive(const std::string& id) const { + DCHECK(thread_checker_.CalledOnValidThread()); + return activity_data_service_ + ? activity_data_service_->GetDaysSinceLastActive(id) + : kDaysUnknown; +} + +void PersistedData::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterDictionaryPref(kPersistedDataPreference); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/persisted_data.h b/src/updater_components/update_client/persisted_data.h new file mode 100644 index 0000000..d35bd15 --- /dev/null +++ b/src/updater_components/update_client/persisted_data.h
@@ -0,0 +1,118 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PERSISTED_DATA_H_ +#define COMPONENTS_UPDATE_CLIENT_PERSISTED_DATA_H_ + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/threading/thread_checker.h" +#include "base/values.h" + +class PrefRegistrySimple; +class PrefService; + +namespace update_client { + +class ActivityDataService; + +// A PersistedData is a wrapper layer around a PrefService, designed to maintain +// update data that outlives the browser process and isn't exposed outside of +// update_client. +// +// The public methods of this class should be called only on the thread that +// initializes it - which also has to match the thread the PrefService has been +// initialized on. +class PersistedData { + public: + // Constructs a provider using the specified |pref_service| and + // |activity_data_service|. + // The associated preferences are assumed to already be registered. + // The |pref_service| and |activity_data_service| must outlive the entire + // update_client. + PersistedData(PrefService* pref_service, + ActivityDataService* activity_data_service); + + ~PersistedData(); + + // Returns the DateLastRollCall (the server-localized calendar date number the + // |id| was last checked by this client on) for the specified |id|. + // -2 indicates that there is no recorded date number for the |id|. + int GetDateLastRollCall(const std::string& id) const; + + // Returns the DateLastActive (the server-localized calendar date number the + // |id| was last active by this client on) for the specified |id|. + // -1 indicates that there is no recorded date for the |id| (i.e. this is the + // first time the |id| is active). + // -2 indicates that the |id| has an unknown value of last active date. + int GetDateLastActive(const std::string& id) const; + + // Returns the PingFreshness (a random token that is written into the profile + // data whenever the DateLastRollCall it is modified) for the specified |id|. + // "" indicates that there is no recorded freshness value for the |id|. + std::string GetPingFreshness(const std::string& id) const; + + // Records the DateLastRollCall for the specified |ids|. |datenum| must be a + // non-negative integer: calls with a negative |datenum| are simply ignored. + // Calls to SetDateLastRollCall that occur prior to the persisted data store + // has been fully initialized are ignored. Also sets the PingFreshness. + void SetDateLastRollCall(const std::vector<std::string>& ids, int datenum); + + // Records the DateLastActive for the specified |ids|. |datenum| must be a + // non-negative integer: calls with a negative |datenum| are simply ignored. + // Calls to SetDateLastActive that occur prior to the persisted data store + // has been fully initialized or the active bit of the |ids| are not set + // are ignored. + // This function also clears the active bits of the specified |ids| if they + // are set. + void SetDateLastActive(const std::vector<std::string>& ids, int datenum); + + // This is called only via update_client's RegisterUpdateClientPreferences. + static void RegisterPrefs(PrefRegistrySimple* registry); + + // These functions return cohort data for the specified |id|. "Cohort" + // indicates the membership of the client in any release channels components + // have set up in a machine-readable format, while "CohortName" does so in a + // human-readable form. "CohortHint" indicates the client's channel selection + // preference. + std::string GetCohort(const std::string& id) const; + std::string GetCohortHint(const std::string& id) const; + std::string GetCohortName(const std::string& id) const; + + // These functions set cohort data for the specified |id|. + void SetCohort(const std::string& id, const std::string& cohort); + void SetCohortHint(const std::string& id, const std::string& cohort_hint); + void SetCohortName(const std::string& id, const std::string& cohort_name); + + // Returns true if the active bit of the specified |id| is set. + bool GetActiveBit(const std::string& id) const; + + // The following two functions returns the number of days since the last + // time the client checked for update/was active. + // -1 indicates that this is the first time the client reports + // an update check/active for the specified |id|. + // -2 indicates that the client has no information about the + // update check/last active of the specified |id|. + int GetDaysSinceLastRollCall(const std::string& id) const; + int GetDaysSinceLastActive(const std::string& id) const; + + private: + int GetInt(const std::string& id, const std::string& key, int fallback) const; + std::string GetString(const std::string& id, const std::string& key) const; + void SetString(const std::string& id, + const std::string& key, + const std::string& value); + + base::ThreadChecker thread_checker_; + PrefService* pref_service_; + ActivityDataService* activity_data_service_; + + DISALLOW_COPY_AND_ASSIGN(PersistedData); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PERSISTED_DATA_H_
diff --git a/src/updater_components/update_client/persisted_data_unittest.cc b/src/updater_components/update_client/persisted_data_unittest.cc new file mode 100644 index 0000000..7f7ae6e --- /dev/null +++ b/src/updater_components/update_client/persisted_data_unittest.cc
@@ -0,0 +1,250 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "components/prefs/testing_pref_service.h" +#include "components/update_client/activity_data_service.h" +#include "components/update_client/persisted_data.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace update_client { + +TEST(PersistedDataTest, Simple) { + auto pref = std::make_unique<TestingPrefServiceSimple>(); + PersistedData::RegisterPrefs(pref->registry()); + auto metadata = std::make_unique<PersistedData>(pref.get(), nullptr); + EXPECT_EQ(-2, metadata->GetDateLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDateLastActive("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid")); + std::vector<std::string> items; + items.push_back("someappid"); + metadata->SetDateLastRollCall(items, 3383); + metadata->SetDateLastActive(items, 3383); + EXPECT_EQ(3383, metadata->GetDateLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDateLastActive("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid")); + EXPECT_EQ(-2, metadata->GetDateLastRollCall("someotherappid")); + EXPECT_EQ(-2, metadata->GetDateLastActive("someotherappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someotherappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someotherappid")); + const std::string pf1 = metadata->GetPingFreshness("someappid"); + EXPECT_FALSE(pf1.empty()); + metadata->SetDateLastRollCall(items, 3386); + metadata->SetDateLastActive(items, 3386); + EXPECT_EQ(3386, metadata->GetDateLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDateLastActive("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid")); + EXPECT_EQ(-2, metadata->GetDateLastRollCall("someotherappid")); + EXPECT_EQ(-2, metadata->GetDateLastActive("someotherappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someotherappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someotherappid")); + const std::string pf2 = metadata->GetPingFreshness("someappid"); + EXPECT_FALSE(pf2.empty()); + // The following has a 1 / 2^128 chance of being flaky. + EXPECT_NE(pf1, pf2); +} + +TEST(PersistedDataTest, SharedPref) { + auto pref = std::make_unique<TestingPrefServiceSimple>(); + PersistedData::RegisterPrefs(pref->registry()); + auto metadata = std::make_unique<PersistedData>(pref.get(), nullptr); + EXPECT_EQ(-2, metadata->GetDateLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDateLastActive("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid")); + std::vector<std::string> items; + items.push_back("someappid"); + metadata->SetDateLastRollCall(items, 3383); + metadata->SetDateLastActive(items, 3383); + + // Now, create a new PersistedData reading from the same path, verify + // that it loads the value. + metadata = std::make_unique<PersistedData>(pref.get(), nullptr); + EXPECT_EQ(3383, metadata->GetDateLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDateLastActive("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid")); + EXPECT_EQ(-2, metadata->GetDateLastRollCall("someotherappid")); + EXPECT_EQ(-2, metadata->GetDateLastActive("someotherappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someotherappid")); + EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someotherappid")); +} + +TEST(PersistedDataTest, SimpleCohort) { + auto pref = std::make_unique<TestingPrefServiceSimple>(); + PersistedData::RegisterPrefs(pref->registry()); + auto metadata = std::make_unique<PersistedData>(pref.get(), nullptr); + EXPECT_EQ("", metadata->GetCohort("someappid")); + EXPECT_EQ("", metadata->GetCohort("someotherappid")); + EXPECT_EQ("", metadata->GetCohortHint("someappid")); + EXPECT_EQ("", metadata->GetCohortHint("someotherappid")); + EXPECT_EQ("", metadata->GetCohortName("someappid")); + EXPECT_EQ("", metadata->GetCohortName("someotherappid")); + metadata->SetCohort("someappid", "c1"); + metadata->SetCohort("someotherappid", "c2"); + metadata->SetCohortHint("someappid", "ch1"); + metadata->SetCohortHint("someotherappid", "ch2"); + metadata->SetCohortName("someappid", "cn1"); + metadata->SetCohortName("someotherappid", "cn2"); + EXPECT_EQ("c1", metadata->GetCohort("someappid")); + EXPECT_EQ("c2", metadata->GetCohort("someotherappid")); + EXPECT_EQ("ch1", metadata->GetCohortHint("someappid")); + EXPECT_EQ("ch2", metadata->GetCohortHint("someotherappid")); + EXPECT_EQ("cn1", metadata->GetCohortName("someappid")); + EXPECT_EQ("cn2", metadata->GetCohortName("someotherappid")); + metadata->SetCohort("someappid", "oc1"); + metadata->SetCohort("someotherappid", "oc2"); + metadata->SetCohortHint("someappid", "och1"); + metadata->SetCohortHint("someotherappid", "och2"); + metadata->SetCohortName("someappid", "ocn1"); + metadata->SetCohortName("someotherappid", "ocn2"); + EXPECT_EQ("oc1", metadata->GetCohort("someappid")); + EXPECT_EQ("oc2", metadata->GetCohort("someotherappid")); + EXPECT_EQ("och1", metadata->GetCohortHint("someappid")); + EXPECT_EQ("och2", metadata->GetCohortHint("someotherappid")); + EXPECT_EQ("ocn1", metadata->GetCohortName("someappid")); + EXPECT_EQ("ocn2", metadata->GetCohortName("someotherappid")); +} + +TEST(PersistedDataTest, ActivityData) { + class TestActivityDataService : public ActivityDataService { + public: + bool GetActiveBit(const std::string& id) const override { + auto it = active_.find(id); + return it != active_.end() ? it->second : false; + } + + int GetDaysSinceLastActive(const std::string& id) const override { + auto it = days_last_active_.find(id); + return it != days_last_active_.end() ? it->second : -1; + } + + int GetDaysSinceLastRollCall(const std::string& id) const override { + auto it = days_last_roll_call_.find(id); + return it != days_last_roll_call_.end() ? it->second : -1; + } + + void ClearActiveBit(const std::string& id) override { active_[id] = false; } + + void SetDaysSinceLastActive(const std::string& id, int numdays) { + days_last_active_[id] = numdays; + } + + void SetDaysSinceLastRollCall(const std::string& id, int numdays) { + days_last_roll_call_[id] = numdays; + } + + void SetActiveBit(const std::string& id) { active_[id] = true; } + + private: + std::map<std::string, bool> active_; + std::map<std::string, int> days_last_active_; + std::map<std::string, int> days_last_roll_call_; + }; + + auto pref = std::make_unique<TestingPrefServiceSimple>(); + auto activity_service = std::make_unique<TestActivityDataService>(); + PersistedData::RegisterPrefs(pref->registry()); + auto metadata = + std::make_unique<PersistedData>(pref.get(), activity_service.get()); + + std::vector<std::string> items({"id1", "id2", "id3"}); + + for (const auto& item : items) { + EXPECT_EQ(-2, metadata->GetDateLastActive(item)); + EXPECT_EQ(-2, metadata->GetDateLastRollCall(item)); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive(item)); + EXPECT_EQ(-1, metadata->GetDaysSinceLastRollCall(item)); + EXPECT_EQ(false, metadata->GetActiveBit(item)); + } + + metadata->SetDateLastActive(items, 1234); + metadata->SetDateLastRollCall(items, 1234); + for (const auto& item : items) { + EXPECT_EQ(false, metadata->GetActiveBit(item)); + EXPECT_EQ(-2, metadata->GetDateLastActive(item)); + EXPECT_EQ(1234, metadata->GetDateLastRollCall(item)); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive(item)); + EXPECT_EQ(-1, metadata->GetDaysSinceLastRollCall(item)); + } + + activity_service->SetActiveBit("id1"); + activity_service->SetDaysSinceLastActive("id1", 3); + activity_service->SetDaysSinceLastRollCall("id1", 2); + activity_service->SetDaysSinceLastRollCall("id2", 3); + activity_service->SetDaysSinceLastRollCall("id3", 4); + EXPECT_EQ(true, metadata->GetActiveBit("id1")); + EXPECT_EQ(false, metadata->GetActiveBit("id2")); + EXPECT_EQ(false, metadata->GetActiveBit("id2")); + EXPECT_EQ(3, metadata->GetDaysSinceLastActive("id1")); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id2")); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id3")); + EXPECT_EQ(2, metadata->GetDaysSinceLastRollCall("id1")); + EXPECT_EQ(3, metadata->GetDaysSinceLastRollCall("id2")); + EXPECT_EQ(4, metadata->GetDaysSinceLastRollCall("id3")); + + metadata->SetDateLastActive(items, 3384); + metadata->SetDateLastRollCall(items, 3383); + EXPECT_EQ(false, metadata->GetActiveBit("id1")); + EXPECT_EQ(false, metadata->GetActiveBit("id2")); + EXPECT_EQ(false, metadata->GetActiveBit("id3")); + EXPECT_EQ(3, metadata->GetDaysSinceLastActive("id1")); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id2")); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id3")); + EXPECT_EQ(2, metadata->GetDaysSinceLastRollCall("id1")); + EXPECT_EQ(3, metadata->GetDaysSinceLastRollCall("id2")); + EXPECT_EQ(4, metadata->GetDaysSinceLastRollCall("id3")); + EXPECT_EQ(3384, metadata->GetDateLastActive("id1")); + EXPECT_EQ(-2, metadata->GetDateLastActive("id2")); + EXPECT_EQ(-2, metadata->GetDateLastActive("id3")); + EXPECT_EQ(3383, metadata->GetDateLastRollCall("id1")); + EXPECT_EQ(3383, metadata->GetDateLastRollCall("id2")); + EXPECT_EQ(3383, metadata->GetDateLastRollCall("id3")); + + metadata->SetDateLastActive(items, 5000); + metadata->SetDateLastRollCall(items, 3390); + EXPECT_EQ(false, metadata->GetActiveBit("id1")); + EXPECT_EQ(false, metadata->GetActiveBit("id2")); + EXPECT_EQ(false, metadata->GetActiveBit("id3")); + EXPECT_EQ(3, metadata->GetDaysSinceLastActive("id1")); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id2")); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id3")); + EXPECT_EQ(2, metadata->GetDaysSinceLastRollCall("id1")); + EXPECT_EQ(3, metadata->GetDaysSinceLastRollCall("id2")); + EXPECT_EQ(4, metadata->GetDaysSinceLastRollCall("id3")); + EXPECT_EQ(3384, metadata->GetDateLastActive("id1")); + EXPECT_EQ(-2, metadata->GetDateLastActive("id2")); + EXPECT_EQ(-2, metadata->GetDateLastActive("id3")); + EXPECT_EQ(3390, metadata->GetDateLastRollCall("id1")); + EXPECT_EQ(3390, metadata->GetDateLastRollCall("id2")); + EXPECT_EQ(3390, metadata->GetDateLastRollCall("id3")); + + activity_service->SetActiveBit("id2"); + metadata->SetDateLastActive(items, 5678); + metadata->SetDateLastRollCall(items, 6789); + EXPECT_EQ(false, metadata->GetActiveBit("id1")); + EXPECT_EQ(false, metadata->GetActiveBit("id2")); + EXPECT_EQ(false, metadata->GetActiveBit("id3")); + EXPECT_EQ(3, metadata->GetDaysSinceLastActive("id1")); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id2")); + EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id3")); + EXPECT_EQ(2, metadata->GetDaysSinceLastRollCall("id1")); + EXPECT_EQ(3, metadata->GetDaysSinceLastRollCall("id2")); + EXPECT_EQ(4, metadata->GetDaysSinceLastRollCall("id3")); + EXPECT_EQ(3384, metadata->GetDateLastActive("id1")); + EXPECT_EQ(5678, metadata->GetDateLastActive("id2")); + EXPECT_EQ(-2, metadata->GetDateLastActive("id3")); + EXPECT_EQ(6789, metadata->GetDateLastRollCall("id1")); + EXPECT_EQ(6789, metadata->GetDateLastRollCall("id2")); + EXPECT_EQ(6789, metadata->GetDateLastRollCall("id3")); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/ping_manager.cc b/src/updater_components/update_client/ping_manager.cc new file mode 100644 index 0000000..3c2a60c --- /dev/null +++ b/src/updater_components/update_client/ping_manager.cc
@@ -0,0 +1,129 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/ping_manager.h" + +#include <stddef.h> + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/update_client/component.h" +#include "components/update_client/configurator.h" +#include "components/update_client/protocol_definition.h" +#include "components/update_client/protocol_handler.h" +#include "components/update_client/protocol_serializer.h" +#include "components/update_client/request_sender.h" +#include "components/update_client/utils.h" +#include "url/gurl.h" + +namespace update_client { + +namespace { + +const int kErrorNoEvents = -1; +const int kErrorNoUrl = -2; + +// An instance of this class can send only one ping. +class PingSender : public base::RefCountedThreadSafe<PingSender> { + public: + using Callback = PingManager::Callback; + explicit PingSender(scoped_refptr<Configurator> config); + void SendPing(const Component& component, Callback callback); + + protected: + virtual ~PingSender(); + + private: + friend class base::RefCountedThreadSafe<PingSender>; + void SendPingComplete(int error, + const std::string& response, + int retry_after_sec); + + THREAD_CHECKER(thread_checker_); + + const scoped_refptr<Configurator> config_; + Callback callback_; + std::unique_ptr<RequestSender> request_sender_; + + DISALLOW_COPY_AND_ASSIGN(PingSender); +}; + +PingSender::PingSender(scoped_refptr<Configurator> config) : config_(config) {} + +PingSender::~PingSender() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +} + +void PingSender::SendPing(const Component& component, Callback callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (component.events().empty()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), kErrorNoEvents, "")); + return; + } + + DCHECK(component.crx_component()); + + auto urls(config_->PingUrl()); + if (component.crx_component()->requires_network_encryption) + RemoveUnsecureUrls(&urls); + + if (urls.empty()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), kErrorNoUrl, "")); + return; + } + + callback_ = std::move(callback); + + std::vector<protocol_request::App> apps; + apps.push_back(MakeProtocolApp(component.id(), + component.crx_component()->version, + component.GetEvents())); + request_sender_ = std::make_unique<RequestSender>(config_); + request_sender_->Send( + urls, {}, + config_->GetProtocolHandlerFactory()->CreateSerializer()->Serialize( + MakeProtocolRequest( + component.session_id(), config_->GetProdId(), + config_->GetBrowserVersion().GetString(), config_->GetLang(), + config_->GetChannel(), config_->GetOSLongName(), + config_->GetDownloadPreference(), config_->ExtraRequestParams(), + nullptr, std::move(apps))), + false, base::BindOnce(&PingSender::SendPingComplete, this)); +} + +void PingSender::SendPingComplete(int error, + const std::string& response, + int retry_after_sec) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + std::move(callback_).Run(error, response); +} + +} // namespace + +PingManager::PingManager(scoped_refptr<Configurator> config) + : config_(config) {} + +PingManager::~PingManager() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +} + +void PingManager::SendPing(const Component& component, Callback callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + auto ping_sender = base::MakeRefCounted<PingSender>(config_); + ping_sender->SendPing(component, std::move(callback)); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/ping_manager.h b/src/updater_components/update_client/ping_manager.h new file mode 100644 index 0000000..24040e6 --- /dev/null +++ b/src/updater_components/update_client/ping_manager.h
@@ -0,0 +1,48 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PING_MANAGER_H_ +#define COMPONENTS_UPDATE_CLIENT_PING_MANAGER_H_ + +#include <string> + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" + +namespace update_client { + +class Configurator; +class Component; + +class PingManager : public base::RefCountedThreadSafe<PingManager> { + public: + // |error| is 0 if the ping was sent successfully, otherwise |error| contains + // a value with no particular meaning for the caller. + using Callback = + base::OnceCallback<void(int error, const std::string& response)>; + + explicit PingManager(scoped_refptr<Configurator> config); + + // Sends a ping for the |item|. |callback| is invoked after the ping is sent + // or an error has occured. The ping itself is not persisted and it will + // be discarded if it has not been sent for any reason. + virtual void SendPing(const Component& component, Callback callback); + + protected: + virtual ~PingManager(); + + private: + friend class base::RefCountedThreadSafe<PingManager>; + + THREAD_CHECKER(thread_checker_); + const scoped_refptr<Configurator> config_; + + DISALLOW_COPY_AND_ASSIGN(PingManager); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PING_MANAGER_H_
diff --git a/src/updater_components/update_client/ping_manager_unittest.cc b/src/updater_components/update_client/ping_manager_unittest.cc new file mode 100644 index 0000000..0277b9b --- /dev/null +++ b/src/updater_components/update_client/ping_manager_unittest.cc
@@ -0,0 +1,442 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/ping_manager.h" + +#include <stdint.h> + +#include <limits> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/json/json_reader.h" +#include "base/memory/ref_counted.h" +#include "base/run_loop.h" +#include "base/test/task_environment.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/version.h" +#include "components/update_client/component.h" +#include "components/update_client/net/url_loader_post_interceptor.h" +#include "components/update_client/protocol_definition.h" +#include "components/update_client/protocol_serializer.h" +#include "components/update_client/test_configurator.h" +#include "components/update_client/update_engine.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/re2/src/re2/re2.h" + +using std::string; + +namespace update_client { + +class PingManagerTest : public testing::Test, + public testing::WithParamInterface<bool> { + public: + PingManagerTest(); + ~PingManagerTest() override {} + + PingManager::Callback MakePingCallback(); + scoped_refptr<UpdateContext> MakeMockUpdateContext() const; + + // Overrides from testing::Test. + void SetUp() override; + void TearDown() override; + + void PingSentCallback(int error, const std::string& response); + + protected: + void Quit(); + void RunThreads(); + + scoped_refptr<TestConfigurator> config_; + scoped_refptr<PingManager> ping_manager_; + + int error_ = -1; + std::string response_; + + private: + base::test::TaskEnvironment task_environment_; + base::OnceClosure quit_closure_; +}; + +PingManagerTest::PingManagerTest() + : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) { + config_ = base::MakeRefCounted<TestConfigurator>(); +} + +void PingManagerTest::SetUp() { + ping_manager_ = base::MakeRefCounted<PingManager>(config_); +} + +void PingManagerTest::TearDown() { + // Run the threads until they are idle to allow the clean up + // of the network interceptors on the IO thread. + task_environment_.RunUntilIdle(); + ping_manager_ = nullptr; +} + +void PingManagerTest::RunThreads() { + base::RunLoop runloop; + quit_closure_ = runloop.QuitClosure(); + runloop.Run(); +} + +void PingManagerTest::Quit() { + if (!quit_closure_.is_null()) + std::move(quit_closure_).Run(); +} + +PingManager::Callback PingManagerTest::MakePingCallback() { + return base::BindOnce(&PingManagerTest::PingSentCallback, + base::Unretained(this)); +} + +void PingManagerTest::PingSentCallback(int error, const std::string& response) { + error_ = error; + response_ = response; + Quit(); +} + +scoped_refptr<UpdateContext> PingManagerTest::MakeMockUpdateContext() const { + return base::MakeRefCounted<UpdateContext>( + config_, false, std::vector<std::string>(), + UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(), + UpdateEngine::Callback(), nullptr); +} + +// This test is parameterized for using JSON or XML serialization. |true| means +// JSON serialization is used. +INSTANTIATE_TEST_SUITE_P(Parameterized, PingManagerTest, testing::Bool()); + +TEST_P(PingManagerTest, SendPing) { + auto interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(interceptor); + + const auto update_context = MakeMockUpdateContext(); + { + // Test eventresult="1" is sent for successful updates. + Component component(*update_context, "abc"); + component.crx_component_ = CrxComponent(); + component.crx_component_->version = base::Version("1.0"); + component.state_ = std::make_unique<Component::StateUpdated>(&component); + component.previous_version_ = base::Version("1.0"); + component.next_version_ = base::Version("2.0"); + component.AppendEvent(component.MakeEventUpdateComplete()); + + EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>())); + ping_manager_->SendPing(component, MakePingCallback()); + RunThreads(); + + EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString(); + const auto msg = interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(msg); + ASSERT_TRUE(root); + const auto* request = root->FindKey("request"); + ASSERT_TRUE(request); + EXPECT_TRUE(request->FindKey("@os")); + EXPECT_EQ("fake_prodid", request->FindKey("@updater")->GetString()); + EXPECT_EQ("crx2,crx3", request->FindKey("acceptformat")->GetString()); + EXPECT_TRUE(request->FindKey("arch")); + EXPECT_EQ("cr", request->FindKey("dedup")->GetString()); + EXPECT_LT(0, request->FindPath({"hw", "physmemory"})->GetInt()); + EXPECT_EQ("fake_lang", request->FindKey("lang")->GetString()); + EXPECT_TRUE(request->FindKey("nacl_arch")); + EXPECT_EQ("fake_channel_string", + request->FindKey("prodchannel")->GetString()); + EXPECT_EQ("30.0", request->FindKey("prodversion")->GetString()); + EXPECT_EQ("3.1", request->FindKey("protocol")->GetString()); + EXPECT_TRUE(request->FindKey("requestid")); + EXPECT_TRUE(request->FindKey("sessionid")); + EXPECT_EQ("fake_channel_string", + request->FindKey("updaterchannel")->GetString()); + EXPECT_EQ("30.0", request->FindKey("updaterversion")->GetString()); + + EXPECT_TRUE(request->FindPath({"os", "arch"})->is_string()); + EXPECT_EQ("Fake Operating System", + request->FindPath({"os", "platform"})->GetString()); + EXPECT_TRUE(request->FindPath({"os", "version"})->is_string()); + + const auto& app = request->FindKey("app")->GetList()[0]; + EXPECT_EQ("abc", app.FindKey("appid")->GetString()); + EXPECT_EQ("1.0", app.FindKey("version")->GetString()); + const auto& event = app.FindKey("event")->GetList()[0]; + EXPECT_EQ(1, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(3, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString()); + EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString()); + + // Check the ping request does not carry the specific extra request headers. + const auto headers = std::get<1>(interceptor->GetRequests()[0]); + EXPECT_FALSE(headers.HasHeader("X-Goog-Update-Interactivity")); + EXPECT_FALSE(headers.HasHeader("X-Goog-Update-Updater")); + EXPECT_FALSE(headers.HasHeader("X-Goog-Update-AppId")); + interceptor->Reset(); + } + + { + // Test eventresult="0" is sent for failed updates. + Component component(*update_context, "abc"); + component.crx_component_ = CrxComponent(); + component.crx_component_->version = base::Version("1.0"); + component.state_ = + std::make_unique<Component::StateUpdateError>(&component); + component.previous_version_ = base::Version("1.0"); + component.next_version_ = base::Version("2.0"); + component.AppendEvent(component.MakeEventUpdateComplete()); + + EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>())); + ping_manager_->SendPing(component, MakePingCallback()); + RunThreads(); + + EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString(); + const auto msg = interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(msg); + ASSERT_TRUE(root); + const auto* request = root->FindKey("request"); + const auto& app = request->FindKey("app")->GetList()[0]; + EXPECT_EQ("abc", app.FindKey("appid")->GetString()); + EXPECT_EQ("1.0", app.FindKey("version")->GetString()); + const auto& event = app.FindKey("event")->GetList()[0]; + EXPECT_EQ(0, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(3, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString()); + EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString()); + interceptor->Reset(); + } + + { + // Test the error values and the fingerprints. + Component component(*update_context, "abc"); + component.crx_component_ = CrxComponent(); + component.crx_component_->version = base::Version("1.0"); + component.state_ = + std::make_unique<Component::StateUpdateError>(&component); + component.previous_version_ = base::Version("1.0"); + component.next_version_ = base::Version("2.0"); + component.previous_fp_ = "prev fp"; + component.next_fp_ = "next fp"; + component.error_category_ = ErrorCategory::kDownload; + component.error_code_ = 2; + component.extra_code1_ = -1; + component.diff_error_category_ = ErrorCategory::kService; + component.diff_error_code_ = 20; + component.diff_extra_code1_ = -10; + component.crx_diffurls_.push_back(GURL("http://host/path")); + component.AppendEvent(component.MakeEventUpdateComplete()); + + EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>())); + ping_manager_->SendPing(component, MakePingCallback()); + RunThreads(); + + EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString(); + const auto msg = interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(msg); + ASSERT_TRUE(root); + const auto* request = root->FindKey("request"); + const auto& app = request->FindKey("app")->GetList()[0]; + EXPECT_EQ("abc", app.FindKey("appid")->GetString()); + EXPECT_EQ("1.0", app.FindKey("version")->GetString()); + const auto& event = app.FindKey("event")->GetList()[0]; + EXPECT_EQ(0, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(3, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString()); + EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString()); + EXPECT_EQ(4, event.FindKey("differrorcat")->GetInt()); + EXPECT_EQ(20, event.FindKey("differrorcode")->GetInt()); + EXPECT_EQ(-10, event.FindKey("diffextracode1")->GetInt()); + EXPECT_EQ(0, event.FindKey("diffresult")->GetInt()); + EXPECT_EQ(1, event.FindKey("errorcat")->GetInt()); + EXPECT_EQ(2, event.FindKey("errorcode")->GetInt()); + EXPECT_EQ(-1, event.FindKey("extracode1")->GetInt()); + EXPECT_EQ("next fp", event.FindKey("nextfp")->GetString()); + EXPECT_EQ("prev fp", event.FindKey("previousfp")->GetString()); + interceptor->Reset(); + } + + { + // Test an invalid |next_version| is not serialized. + Component component(*update_context, "abc"); + component.crx_component_ = CrxComponent(); + component.crx_component_->version = base::Version("1.0"); + component.state_ = + std::make_unique<Component::StateUpdateError>(&component); + component.previous_version_ = base::Version("1.0"); + + component.AppendEvent(component.MakeEventUpdateComplete()); + + EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>())); + ping_manager_->SendPing(component, MakePingCallback()); + RunThreads(); + + EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString(); + const auto msg = interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(msg); + ASSERT_TRUE(root); + const auto* request = root->FindKey("request"); + const auto& app = request->FindKey("app")->GetList()[0]; + EXPECT_EQ("abc", app.FindKey("appid")->GetString()); + EXPECT_EQ("1.0", app.FindKey("version")->GetString()); + const auto& event = app.FindKey("event")->GetList()[0]; + EXPECT_EQ(0, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(3, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString()); + interceptor->Reset(); + } + + { + // Test a valid |previouversion| and |next_version| = base::Version("0") + // are serialized correctly under <event...> for uninstall. + Component component(*update_context, "abc"); + component.crx_component_ = CrxComponent(); + component.Uninstall(base::Version("1.2.3.4"), 0); + component.AppendEvent(component.MakeEventUninstalled()); + + EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>())); + ping_manager_->SendPing(component, MakePingCallback()); + RunThreads(); + + EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString(); + const auto msg = interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(msg); + ASSERT_TRUE(root); + const auto* request = root->FindKey("request"); + const auto& app = request->FindKey("app")->GetList()[0]; + EXPECT_EQ("abc", app.FindKey("appid")->GetString()); + EXPECT_EQ("1.2.3.4", app.FindKey("version")->GetString()); + const auto& event = app.FindKey("event")->GetList()[0]; + EXPECT_EQ(1, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(4, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ("1.2.3.4", event.FindKey("previousversion")->GetString()); + EXPECT_EQ("0", event.FindKey("nextversion")->GetString()); + interceptor->Reset(); + } + + { + // Test the download metrics. + Component component(*update_context, "abc"); + component.crx_component_ = CrxComponent(); + component.crx_component_->version = base::Version("1.0"); + component.state_ = std::make_unique<Component::StateUpdated>(&component); + component.previous_version_ = base::Version("1.0"); + component.next_version_ = base::Version("2.0"); + component.AppendEvent(component.MakeEventUpdateComplete()); + + CrxDownloader::DownloadMetrics download_metrics; + download_metrics.url = GURL("http://host1/path1"); + download_metrics.downloader = CrxDownloader::DownloadMetrics::kUrlFetcher; + download_metrics.error = -1; + download_metrics.downloaded_bytes = 123; + download_metrics.total_bytes = 456; + download_metrics.download_time_ms = 987; + component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics)); + + download_metrics = CrxDownloader::DownloadMetrics(); + download_metrics.url = GURL("http://host2/path2"); + download_metrics.downloader = CrxDownloader::DownloadMetrics::kBits; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 1230; + download_metrics.total_bytes = 4560; + download_metrics.download_time_ms = 9870; + component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics)); + + download_metrics = CrxDownloader::DownloadMetrics(); + download_metrics.url = GURL("http://host3/path3"); + download_metrics.downloader = CrxDownloader::DownloadMetrics::kBits; + download_metrics.error = 0; + download_metrics.downloaded_bytes = kProtocolMaxInt; + download_metrics.total_bytes = kProtocolMaxInt - 1; + download_metrics.download_time_ms = kProtocolMaxInt - 2; + component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics)); + + EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>())); + ping_manager_->SendPing(component, MakePingCallback()); + RunThreads(); + + EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString(); + const auto msg = interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(msg); + ASSERT_TRUE(root); + const auto* request = root->FindKey("request"); + const auto& app = request->FindKey("app")->GetList()[0]; + EXPECT_EQ("abc", app.FindKey("appid")->GetString()); + EXPECT_EQ("1.0", app.FindKey("version")->GetString()); + EXPECT_EQ(4u, app.FindKey("event")->GetList().size()); + { + const auto& event = app.FindKey("event")->GetList()[0]; + EXPECT_EQ(1, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(3, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString()); + EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString()); + } + { + const auto& event = app.FindKey("event")->GetList()[1]; + EXPECT_EQ(0, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(14, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ(987, event.FindKey("download_time_ms")->GetDouble()); + EXPECT_EQ(123, event.FindKey("downloaded")->GetDouble()); + EXPECT_EQ("direct", event.FindKey("downloader")->GetString()); + EXPECT_EQ(-1, event.FindKey("errorcode")->GetInt()); + EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString()); + EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString()); + EXPECT_EQ(456, event.FindKey("total")->GetDouble()); + EXPECT_EQ("http://host1/path1", event.FindKey("url")->GetString()); + } + { + const auto& event = app.FindKey("event")->GetList()[2]; + EXPECT_EQ(1, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(14, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ(9870, event.FindKey("download_time_ms")->GetDouble()); + EXPECT_EQ(1230, event.FindKey("downloaded")->GetDouble()); + EXPECT_EQ("bits", event.FindKey("downloader")->GetString()); + EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString()); + EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString()); + EXPECT_EQ(4560, event.FindKey("total")->GetDouble()); + EXPECT_EQ("http://host2/path2", event.FindKey("url")->GetString()); + } + { + const auto& event = app.FindKey("event")->GetList()[3]; + EXPECT_EQ(1, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(14, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ(9007199254740990, + event.FindKey("download_time_ms")->GetDouble()); + EXPECT_EQ(9007199254740992, event.FindKey("downloaded")->GetDouble()); + EXPECT_EQ("bits", event.FindKey("downloader")->GetString()); + EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString()); + EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString()); + EXPECT_EQ(9007199254740991, event.FindKey("total")->GetDouble()); + EXPECT_EQ("http://host3/path3", event.FindKey("url")->GetString()); + } + interceptor->Reset(); + } +} + +// Tests that sending the ping fails when the component requires encryption but +// the ping URL is unsecure. +TEST_P(PingManagerTest, RequiresEncryption) { + config_->SetPingUrl(GURL("http:\\foo\bar")); + + const auto update_context = MakeMockUpdateContext(); + + Component component(*update_context, "abc"); + component.crx_component_ = CrxComponent(); + component.crx_component_->version = base::Version("1.0"); + + // The default value for |requires_network_encryption| is true. + EXPECT_TRUE(component.crx_component_->requires_network_encryption); + + component.state_ = std::make_unique<Component::StateUpdated>(&component); + component.previous_version_ = base::Version("1.0"); + component.next_version_ = base::Version("2.0"); + component.AppendEvent(component.MakeEventUpdateComplete()); + + ping_manager_->SendPing(component, MakePingCallback()); + RunThreads(); + + EXPECT_EQ(-2, error_); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/protocol_definition.cc b/src/updater_components/update_client/protocol_definition.cc new file mode 100644 index 0000000..2dd3cc6 --- /dev/null +++ b/src/updater_components/update_client/protocol_definition.cc
@@ -0,0 +1,38 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/protocol_definition.h" + +#include "base/values.h" + +namespace update_client { + +namespace protocol_request { + +OS::OS() = default; +OS::OS(OS&&) = default; +OS::~OS() = default; + +Updater::Updater() = default; +Updater::Updater(const Updater&) = default; +Updater::~Updater() = default; + +UpdateCheck::UpdateCheck() = default; +UpdateCheck::~UpdateCheck() = default; + +Ping::Ping() = default; +Ping::Ping(const Ping&) = default; +Ping::~Ping() = default; + +App::App() = default; +App::App(App&&) = default; +App::~App() = default; + +Request::Request() = default; +Request::Request(Request&&) = default; +Request::~Request() = default; + +} // namespace protocol_request + +} // namespace update_client
diff --git a/src/updater_components/update_client/protocol_definition.h b/src/updater_components/update_client/protocol_definition.h new file mode 100644 index 0000000..aaacc76 --- /dev/null +++ b/src/updater_components/update_client/protocol_definition.h
@@ -0,0 +1,177 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PROTOCOL_DEFINITION_H_ +#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_DEFINITION_H_ + +#include <stdint.h> + +#include <string> +#include <vector> + +#include "base/containers/flat_map.h" +#include "base/macros.h" +#include "base/optional.h" +#include "base/values.h" +#include "build/build_config.h" + +namespace update_client { + +// The protocol versions so far are: +// * Version 3.1: it changes how the run actions are serialized. +// * Version 3.0: it is the version implemented by the desktop updaters. +constexpr char kProtocolVersion[] = "3.1"; + +// Due to implementation constraints of the JSON parser and serializer, +// precision of integer numbers greater than 2^53 is lost. +constexpr int64_t kProtocolMaxInt = 1LL << 53; + +namespace protocol_request { + +struct HW { + uint32_t physmemory = 0; // Physical memory rounded down to the closest GB. +}; + +struct OS { + OS(); + OS(OS&&); + ~OS(); + + std::string platform; + std::string version; + std::string service_pack; + std::string arch; + + private: + DISALLOW_COPY_AND_ASSIGN(OS); +}; + +struct Updater { + Updater(); + Updater(const Updater&); + ~Updater(); + + std::string name; + std::string version; + bool is_machine = false; + bool autoupdate_check_enabled = false; + base::Optional<int> last_started; + base::Optional<int> last_checked; + int update_policy = 0; +}; + +struct UpdateCheck { + UpdateCheck(); + ~UpdateCheck(); + + bool is_update_disabled = false; +}; + +// didrun element. The element is named "ping" for legacy reasons. +struct Ping { + Ping(); + Ping(const Ping&); + ~Ping(); + + // Preferred user count metrics ("ad" and "rd"). + base::Optional<int> date_last_active; + base::Optional<int> date_last_roll_call; + + // Legacy user count metrics ("a" and "r"). + base::Optional<int> days_since_last_active_ping; + int days_since_last_roll_call = 0; + + std::string ping_freshness; +}; + +struct App { + App(); + App(App&&); + ~App(); + + std::string app_id; + std::string version; + base::flat_map<std::string, std::string> installer_attributes; + std::string lang; + std::string brand_code; + std::string install_source; + std::string install_location; + std::string fingerprint; + + std::string cohort; // Opaque string. + std::string cohort_hint; // Server may use to move the app to a new cohort. + std::string cohort_name; // Human-readable interpretation of the cohort. + + base::Optional<bool> enabled; + base::Optional<std::vector<int>> disabled_reasons; + + // Optional update check. + base::Optional<UpdateCheck> update_check; + + // Optional 'did run' ping. + base::Optional<Ping> ping; + + // Progress/result pings. + base::Optional<std::vector<base::Value>> events; + + private: + DISALLOW_COPY_AND_ASSIGN(App); +}; + +struct Request { + Request(); + Request(Request&&); + ~Request(); + + std::string protocol_version; + + // Unique identifier for this session, used to correlate multiple requests + // associated with a single update operation. + std::string session_id; + + // Unique identifier for this request, used to associate the same request + // received multiple times on the server. + std::string request_id; + + std::string updatername; + std::string updaterversion; + std::string prodversion; + std::string lang; + std::string updaterchannel; + std::string prodchannel; + std::string operating_system; + std::string arch; + std::string nacl_arch; + +#if defined(OS_WIN) + bool is_wow64 = false; +#endif + + // Provides a hint for what download urls should be returned by server. + // This data member is controlled by group policy settings. + // The only group policy value supported so far is |cacheable|. + std::string dlpref; + + // True if this machine is part of a managed enterprise domain. + base::Optional<bool> domain_joined; + + base::flat_map<std::string, std::string> additional_attributes; + + HW hw; + + OS os; + + base::Optional<Updater> updater; + + std::vector<App> apps; + + private: + DISALLOW_COPY_AND_ASSIGN(Request); +}; + +} // namespace protocol_request + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PROTOCOL_DEFINITION_H_
diff --git a/src/updater_components/update_client/protocol_handler.cc b/src/updater_components/update_client/protocol_handler.cc new file mode 100644 index 0000000..aa547b4 --- /dev/null +++ b/src/updater_components/update_client/protocol_handler.cc
@@ -0,0 +1,21 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/protocol_handler.h" +#include "components/update_client/protocol_parser_json.h" +#include "components/update_client/protocol_serializer_json.h" + +namespace update_client { + +std::unique_ptr<ProtocolParser> ProtocolHandlerFactoryJSON::CreateParser() + const { + return std::make_unique<ProtocolParserJSON>(); +} + +std::unique_ptr<ProtocolSerializer> +ProtocolHandlerFactoryJSON::CreateSerializer() const { + return std::make_unique<ProtocolSerializerJSON>(); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/protocol_handler.h b/src/updater_components/update_client/protocol_handler.h new file mode 100644 index 0000000..57c8cfd --- /dev/null +++ b/src/updater_components/update_client/protocol_handler.h
@@ -0,0 +1,39 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PROTOCOL_HANDLER_H_ +#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_HANDLER_H_ + +#include <memory> + +#include "base/macros.h" + +namespace update_client { + +class ProtocolParser; +class ProtocolSerializer; + +class ProtocolHandlerFactory { + public: + virtual ~ProtocolHandlerFactory() = default; + virtual std::unique_ptr<ProtocolParser> CreateParser() const = 0; + virtual std::unique_ptr<ProtocolSerializer> CreateSerializer() const = 0; + + protected: + ProtocolHandlerFactory() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerFactory); +}; + +class ProtocolHandlerFactoryJSON final : public ProtocolHandlerFactory { + public: + // Overrides for ProtocolHandlerFactory. + std::unique_ptr<ProtocolParser> CreateParser() const override; + std::unique_ptr<ProtocolSerializer> CreateSerializer() const override; +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PROTOCOL_HANDLER_H_
diff --git a/src/updater_components/update_client/protocol_parser.cc b/src/updater_components/update_client/protocol_parser.cc new file mode 100644 index 0000000..7f280c5 --- /dev/null +++ b/src/updater_components/update_client/protocol_parser.cc
@@ -0,0 +1,55 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/protocol_parser.h" +#include "base/strings/stringprintf.h" + +namespace update_client { + +const char ProtocolParser::Result::kCohort[] = "cohort"; +const char ProtocolParser::Result::kCohortHint[] = "cohorthint"; +const char ProtocolParser::Result::kCohortName[] = "cohortname"; + +ProtocolParser::ProtocolParser() = default; +ProtocolParser::~ProtocolParser() = default; + +ProtocolParser::Results::Results() = default; +ProtocolParser::Results::Results(const Results& other) = default; +ProtocolParser::Results::~Results() = default; + +ProtocolParser::Result::Result() = default; +ProtocolParser::Result::Result(const Result& other) = default; +ProtocolParser::Result::~Result() = default; + +ProtocolParser::Result::Manifest::Manifest() = default; +ProtocolParser::Result::Manifest::Manifest(const Manifest& other) = default; +ProtocolParser::Result::Manifest::~Manifest() = default; + +ProtocolParser::Result::Manifest::Package::Package() = default; +ProtocolParser::Result::Manifest::Package::Package(const Package& other) = + default; +ProtocolParser::Result::Manifest::Package::~Package() = default; + +void ProtocolParser::ParseError(const char* details, ...) { + va_list args; + va_start(args, details); + + if (!errors_.empty()) { + errors_ += "\r\n"; + } + + base::StringAppendV(&errors_, details, args); + va_end(args); +} + +bool ProtocolParser::Parse(const std::string& response) { + results_.daystart_elapsed_seconds = kNoDaystart; + results_.daystart_elapsed_days = kNoDaystart; + results_.list.clear(); + errors_.clear(); + + return DoParse(response, &results_); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/protocol_parser.h b/src/updater_components/update_client/protocol_parser.h new file mode 100644 index 0000000..471fd6d --- /dev/null +++ b/src/updater_components/update_client/protocol_parser.h
@@ -0,0 +1,128 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_H_ +#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_H_ + +#include <cstdint> +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "url/gurl.h" + +namespace update_client { + +class ProtocolParser { + public: + // The result of parsing one |app| entity in an update check response. + struct Result { + struct Manifest { + struct Package { + Package(); + Package(const Package& other); + ~Package(); + + // |fingerprint| is optional. It identifies the package, preferably + // with a modified sha256 hash of the package in hex format. + std::string fingerprint; + + // Attributes for the full update. + std::string name; + std::string hash_sha256; + int64_t size = 0; + + // Attributes for the differential update. + std::string namediff; + std::string hashdiff_sha256; + int64_t sizediff = 0; + }; + + Manifest(); + Manifest(const Manifest& other); + ~Manifest(); + + std::string version; + std::string browser_min_version; + std::vector<Package> packages; + }; + + Result(); + Result(const Result& other); + ~Result(); + + std::string extension_id; + + // The updatecheck response status. + std::string status; + + // The list of fallback urls, for full and diff updates respectively. + // These urls are base urls; they don't include the filename. + std::vector<GURL> crx_urls; + std::vector<GURL> crx_diffurls; + + Manifest manifest; + + // The server has instructed the client to set its [key] to [value] for each + // key-value pair in this string. + std::map<std::string, std::string> cohort_attrs; + + // The following are the only allowed keys in |cohort_attrs|. + static const char kCohort[]; + static const char kCohortHint[]; + static const char kCohortName[]; + + // Contains the run action returned by the server as part of an update + // check response. + std::string action_run; + }; + + static const int kNoDaystart = -1; + struct Results { + Results(); + Results(const Results& other); + ~Results(); + + // This will be >= 0, or kNoDaystart if the <daystart> tag was not present. + int daystart_elapsed_seconds = kNoDaystart; + + // This will be >= 0, or kNoDaystart if the <daystart> tag was not present. + int daystart_elapsed_days = kNoDaystart; + std::vector<Result> list; + }; + + static std::unique_ptr<ProtocolParser> Create(); + + virtual ~ProtocolParser(); + + // Parses an update response string into Result data. Returns a bool + // indicating success or failure. On success, the results are available by + // calling results(). In case of success, only results corresponding to + // the update check status |ok| or |noupdate| are included. + // The details for any failures are available by calling errors(). + bool Parse(const std::string& response); + + const Results& results() const { return results_; } + const std::string& errors() const { return errors_; } + + protected: + ProtocolParser(); + + // Appends parse error details to |errors_| string. + void ParseError(const char* details, ...); + + private: + virtual bool DoParse(const std::string& response, Results* results) = 0; + + Results results_; + std::string errors_; + + DISALLOW_COPY_AND_ASSIGN(ProtocolParser); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_H_
diff --git a/src/updater_components/update_client/protocol_parser_json.cc b/src/updater_components/update_client/protocol_parser_json.cc new file mode 100644 index 0000000..eb022e4 --- /dev/null +++ b/src/updater_components/update_client/protocol_parser_json.cc
@@ -0,0 +1,337 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/protocol_parser_json.h" + +#include <utility> + +#include "base/json/json_reader.h" +#include "base/strings/strcat.h" +#include "base/strings/string_util.h" +#include "base/values.h" +#include "base/version.h" +#include "components/update_client/protocol_definition.h" + +namespace update_client { + +namespace { + +bool ParseManifest(const base::Value& manifest_node, + ProtocolParser::Result* result, + std::string* error) { + if (!manifest_node.is_dict()) { + *error = "'manifest' is not a dictionary."; + } + const auto* version = manifest_node.FindKey("version"); + if (!version || !version->is_string()) { + *error = "Missing version for manifest."; + return false; + } + + result->manifest.version = version->GetString(); + if (!base::Version(result->manifest.version).IsValid()) { + *error = + base::StrCat({"Invalid version: '", result->manifest.version, "'."}); + return false; + } + + // Get the optional minimum browser version. + const auto* browser_min_version = manifest_node.FindKey("prodversionmin"); + if (browser_min_version && browser_min_version->is_string()) { + result->manifest.browser_min_version = browser_min_version->GetString(); + if (!base::Version(result->manifest.browser_min_version).IsValid()) { + *error = base::StrCat({"Invalid prodversionmin: '", + result->manifest.browser_min_version, "'."}); + return false; + } + } + + const auto* packages_node = manifest_node.FindKey("packages"); + if (!packages_node || !packages_node->is_dict()) { + *error = "Missing packages in manifest or 'packages' is not a dictionary."; + return false; + } + const auto* package_node = packages_node->FindKey("package"); + if (!package_node || !package_node->is_list()) { + *error = "Missing package in packages."; + return false; + } + + for (const auto& package : package_node->GetList()) { + if (!package.is_dict()) { + *error = "'package' is not a dictionary."; + return false; + } + ProtocolParser::Result::Manifest::Package p; + const auto* name = package.FindKey("name"); + if (!name || !name->is_string()) { + *error = "Missing name for package."; + return false; + } + p.name = name->GetString(); + + const auto* namediff = package.FindKey("namediff"); + if (namediff && namediff->is_string()) + p.namediff = namediff->GetString(); + + const auto* fingerprint = package.FindKey("fp"); + if (fingerprint && fingerprint->is_string()) + p.fingerprint = fingerprint->GetString(); + + const auto* hash_sha256 = package.FindKey("hash_sha256"); + if (hash_sha256 && hash_sha256->is_string()) + p.hash_sha256 = hash_sha256->GetString(); + + const auto* size = package.FindKey("size"); + if (size && (size->is_int() || size->is_double())) { + const auto val = size->GetDouble(); + if (0 <= val && val < kProtocolMaxInt) + p.size = size->GetDouble(); + } + + const auto* hashdiff_sha256 = package.FindKey("hashdiff_sha256"); + if (hashdiff_sha256 && hashdiff_sha256->is_string()) + p.hashdiff_sha256 = hashdiff_sha256->GetString(); + + const auto* sizediff = package.FindKey("sizediff"); + if (sizediff && (sizediff->is_int() || sizediff->is_double())) { + const auto val = sizediff->GetDouble(); + if (0 <= val && val < kProtocolMaxInt) + p.sizediff = sizediff->GetDouble(); + } + + result->manifest.packages.push_back(std::move(p)); + } + + return true; +} + +void ParseActions(const base::Value& actions_node, + ProtocolParser::Result* result) { + if (!actions_node.is_dict()) + return; + + const auto* action_node = actions_node.FindKey("action"); + if (!action_node || !action_node->is_list()) + return; + + const auto& action_list = action_node->GetList(); + if (action_list.empty() || !action_list[0].is_dict()) + return; + + const auto* run = action_list[0].FindKey("run"); + if (run && run->is_string()) + result->action_run = run->GetString(); +} + +bool ParseUrls(const base::Value& urls_node, + ProtocolParser::Result* result, + std::string* error) { + if (!urls_node.is_dict()) { + *error = "'urls' is not a dictionary."; + return false; + } + const auto* url_node = urls_node.FindKey("url"); + if (!url_node || !url_node->is_list()) { + *error = "Missing url on urls."; + return false; + } + + for (const auto& url : url_node->GetList()) { + if (!url.is_dict()) + continue; + const auto* codebase = url.FindKey("codebase"); + if (codebase && codebase->is_string()) { + GURL crx_url(codebase->GetString()); + if (crx_url.is_valid()) + result->crx_urls.push_back(std::move(crx_url)); + } + const auto* codebasediff = url.FindKey("codebasediff"); + if (codebasediff && codebasediff->is_string()) { + GURL crx_diffurl(codebasediff->GetString()); + if (crx_diffurl.is_valid()) + result->crx_diffurls.push_back(std::move(crx_diffurl)); + } + } + + // Expect at least one url for full update. + if (result->crx_urls.empty()) { + *error = "Missing valid url for full update."; + return false; + } + + return true; +} + +bool ParseUpdateCheck(const base::Value& updatecheck_node, + ProtocolParser::Result* result, + std::string* error) { + if (!updatecheck_node.is_dict()) { + *error = "'updatecheck' is not a dictionary."; + return false; + } + const auto* status = updatecheck_node.FindKey("status"); + if (!status || !status->is_string()) { + *error = "Missing status on updatecheck node"; + return false; + } + + result->status = status->GetString(); + if (result->status == "noupdate") { + const auto* actions_node = updatecheck_node.FindKey("actions"); + if (actions_node) + ParseActions(*actions_node, result); + return true; + } + + if (result->status == "ok") { + const auto* actions_node = updatecheck_node.FindKey("actions"); + if (actions_node) + ParseActions(*actions_node, result); + + const auto* urls_node = updatecheck_node.FindKey("urls"); + if (!urls_node) { + *error = "Missing urls on updatecheck."; + return false; + } + + if (!ParseUrls(*urls_node, result, error)) + return false; + + const auto* manifest_node = updatecheck_node.FindKey("manifest"); + if (!manifest_node) { + *error = "Missing manifest on updatecheck."; + return false; + } + return ParseManifest(*manifest_node, result, error); + } + + // Return the |updatecheck| element status as a parsing error. + *error = result->status; + return false; +} + +bool ParseApp(const base::Value& app_node, + ProtocolParser::Result* result, + std::string* error) { + if (!app_node.is_dict()) { + *error = "'app' is not a dictionary."; + return false; + } + for (const auto* cohort_key : + {ProtocolParser::Result::kCohort, ProtocolParser::Result::kCohortHint, + ProtocolParser::Result::kCohortName}) { + const auto* cohort_value = app_node.FindKey(cohort_key); + if (cohort_value && cohort_value->is_string()) + result->cohort_attrs[cohort_key] = cohort_value->GetString(); + } + const auto* appid = app_node.FindKey("appid"); + if (appid && appid->is_string()) + result->extension_id = appid->GetString(); + if (result->extension_id.empty()) { + *error = "Missing appid on app node"; + return false; + } + + // Read the |status| attribute for the app. + // If the status is one of the defined app status error literals, then return + // it in the result as if it were an updatecheck status, then stop parsing, + // and return success. + const auto* status = app_node.FindKey("status"); + if (status && status->is_string()) { + result->status = status->GetString(); + if (result->status == "restricted" || + result->status == "error-unknownApplication" || + result->status == "error-invalidAppId") + return true; + + // If the status was not handled above and the status is not "ok", then + // this must be a status literal that that the parser does not know about. + if (!result->status.empty() && result->status != "ok") { + *error = "Unknown app status"; + return false; + } + } + + DCHECK(result->status.empty() || result->status == "ok"); + const auto* updatecheck_node = app_node.FindKey("updatecheck"); + if (!updatecheck_node) { + *error = "Missing updatecheck on app."; + return false; + } + + return ParseUpdateCheck(*updatecheck_node, result, error); +} + +} // namespace + +bool ProtocolParserJSON::DoParse(const std::string& response_json, + Results* results) { + DCHECK(results); + + if (response_json.empty()) { + ParseError("Empty JSON."); + return false; + } + + // The JSON response contains a prefix to prevent XSSI. + constexpr char kJSONPrefix[] = ")]}'"; + if (!base::StartsWith(response_json, kJSONPrefix, + base::CompareCase::SENSITIVE)) { + ParseError("Missing secure JSON prefix."); + return false; + } + const auto doc = base::JSONReader::Read( + {response_json.begin() + std::char_traits<char>::length(kJSONPrefix), + response_json.end()}); + if (!doc) { + ParseError("JSON read error."); + return false; + } + if (!doc->is_dict()) { + ParseError("JSON document is not a dictionary."); + return false; + } + const auto* response_node = doc->FindKey("response"); + if (!response_node || !response_node->is_dict()) { + ParseError("Missing 'response' element or 'response' is not a dictionary."); + return false; + } + const auto* protocol = response_node->FindKey("protocol"); + if (!protocol || !protocol->is_string()) { + ParseError("Missing/non-string protocol."); + return false; + } + if (protocol->GetString() != kProtocolVersion) { + ParseError("Incorrect protocol. (expected '%s', found '%s')", + kProtocolVersion, protocol->GetString().c_str()); + return false; + } + + const auto* daystart_node = response_node->FindKey("daystart"); + if (daystart_node && daystart_node->is_dict()) { + const auto* elapsed_seconds = daystart_node->FindKey("elapsed_seconds"); + if (elapsed_seconds && elapsed_seconds->is_int()) + results->daystart_elapsed_seconds = elapsed_seconds->GetInt(); + const auto* elapsed_days = daystart_node->FindKey("elapsed_days"); + if (elapsed_days && elapsed_days->is_int()) + results->daystart_elapsed_days = elapsed_days->GetInt(); + } + + const auto* app_node = response_node->FindKey("app"); + if (app_node && app_node->is_list()) { + for (const auto& app : app_node->GetList()) { + Result result; + std::string error; + if (ParseApp(app, &result, &error)) + results->list.push_back(result); + else + ParseError("%s", error.c_str()); + } + } + + return true; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/protocol_parser_json.h b/src/updater_components/update_client/protocol_parser_json.h new file mode 100644 index 0000000..71f96f4 --- /dev/null +++ b/src/updater_components/update_client/protocol_parser_json.h
@@ -0,0 +1,30 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_JSON_H_ +#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_JSON_H_ + +#include <string> + +#include "base/macros.h" +#include "components/update_client/protocol_parser.h" + +namespace update_client { + +// Parses responses for the update protocol version 3. +// (https://github.com/google/omaha/blob/wiki/ServerProtocolV3.md) +class ProtocolParserJSON final : public ProtocolParser { + public: + ProtocolParserJSON() = default; + + private: + // Overrides for ProtocolParser. + bool DoParse(const std::string& response_json, Results* results) override; + + DISALLOW_COPY_AND_ASSIGN(ProtocolParserJSON); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_JSON_H_
diff --git a/src/updater_components/update_client/protocol_parser_json_unittest.cc b/src/updater_components/update_client/protocol_parser_json_unittest.cc new file mode 100644 index 0000000..5bf114a --- /dev/null +++ b/src/updater_components/update_client/protocol_parser_json_unittest.cc
@@ -0,0 +1,506 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/protocol_parser_json.h" + +#include <memory> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace update_client { + +const char* kJSONValid = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://example.com/"}, + {"codebasediff":"http://diff.example.com/"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}} + } + } + ] + }})"; + +const char* kJSONHash = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://example.com/"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "packages":{"package":[{"name":"extension_1_2_3_4.crx", + "hash_sha256":"1234", + "hashdiff_sha256":"5678"}]}} + } + } + ] + }})"; + +const char* kJSONInvalidSizes = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://example.com/"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "packages":{"package":[{"name":"1","size":1234}, + {"name":"2","size":9007199254740991}, + {"name":"3","size":-1234}, + {"name":"4"}, + {"name":"5","size":"-a"}, + {"name":"6","size":-123467890123456789}, + {"name":"7","size":123467890123456789}, + {"name":"8","sizediff":1234}, + {"name":"9","sizediff":9007199254740991}, + {"name":"10","sizediff":-1234}, + {"name":"11","sizediff":"-a"}, + {"name":"12","sizediff":-123467890123456789}, + {"name":"13","sizediff":123467890123456789}]}} + } + } + ] + }})"; + +const char* kJSONInvalidMissingCodebase = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebasediff":"http://diff.example.com"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "packages":{"package":[{"namediff":"extension_1_2_3_4.crx"}]}} + } + } + ] + }})"; + +const char* kJSONInvalidMissingManifest = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://localhost/download/"}]} + } + } + ] + }})"; + +const char* kJSONMissingAppId = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://localhost/download/"}]} + } + } + ] + }})"; + +const char* kJSONInvalidCodebase = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"example.com/extension_1.2.3.4.crx", + "version":"1.2.3.4"}]} + } + } + ] + }})"; + +const char* kJSONMissingVersion = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://localhost/download/"}]}, + "manifest":{ + "packages":{"package":[{"name":"jebgalgnebhfojomionfpkfelancnnkf.crx"}]}} + } + } + ] + }})"; + +const char* kJSONInvalidVersion = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://localhost/download/"}]}, + "manifest":{ + "version":"1.2.3.a", + "packages":{"package":[{"name":"jebgalgnebhfojomionfpkfelancnnkf.crx"}]}} + } + } + ] + }})"; + +// Includes a <daystart> tag. +const char* kJSONWithDaystart = R"()]}' + {"response":{ + "protocol":"3.1", + "daystart":{"elapsed_seconds":456}, + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://example.com/"}, + {"codebasediff":"http://diff.example.com/"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}} + } + } + ] + }})"; + +// Indicates no updates available. +const char* kJSONNoUpdate = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "status":"ok", + "updatecheck":{ + "status":"noupdate" + } + } + ] + }})"; + +// Includes two app objects, one app with an error. +const char* kJSONTwoAppsOneError = R"()]}' + {"response":{ + "protocol":"3.1", + "daystart":{"elapsed_seconds":456}, + "app":[ + {"appid":"aaaaaaaa", + "status":"error-unknownApplication", + "updatecheck":{"status":"error-internal"} + }, + {"appid":"bbbbbbbb", + "status":"ok", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://example.com/"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}} + } + } + ] + }})"; + +// Includes two <app> tags, both of which set the cohort. +const char* kJSONTwoAppsSetCohort = R"()]}' + {"response":{ + "protocol":"3.1", + "daystart":{"elapsed_seconds":456}, + "app":[ + {"appid":"aaaaaaaa", + "cohort":"1:2q3/", + "updatecheck":{"status":"noupdate"} + }, + {"appid":"bbbbbbbb", + "cohort":"1:33z@0.33", + "cohortname":"cname", + "updatecheck":{ + "status":"ok", + "urls":{"url":[{"codebase":"http://example.com/"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}} + } + } + ] + }})"; + +// Includes a run action for an update check with status='ok'. +const char* kJSONUpdateCheckStatusOkWithRunAction = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "updatecheck":{ + "status":"ok", + "actions":{"action":[{"run":"this"}]}, + "urls":{"url":[{"codebase":"http://example.com/"}, + {"codebasediff":"http://diff.example.com/"}]}, + "manifest":{ + "version":"1.2.3.4", + "prodversionmin":"2.0.143.0", + "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}} + } + } + ] + }})"; + +// Includes a run action for an update check with status='noupdate'. +const char* kJSONUpdateCheckStatusNoUpdateWithRunAction = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "updatecheck":{ + "status":"noupdate", + "actions":{"action":[{"run":"this"}]} + } + } + ] + }})"; + +// Includes a run action for an update check with status='error'. +const char* kJSONUpdateCheckStatusErrorWithRunAction = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"12345", + "updatecheck":{ + "status":"error-osnotsupported", + "actions":{"action":[{"run":"this"}]} + } + } + ] + }})"; + +// Includes four app objects with status different than 'ok'. +const char* kJSONAppsStatusError = R"()]}' + {"response":{ + "protocol":"3.1", + "app":[ + {"appid":"aaaaaaaa", + "status":"error-unknownApplication", + "updatecheck":{"status":"error-internal"} + }, + {"appid":"bbbbbbbb", + "status":"restricted", + "updatecheck":{"status":"error-internal"} + }, + {"appid":"cccccccc", + "status":"error-invalidAppId", + "updatecheck":{"status":"error-internal"} + }, + {"appid":"dddddddd", + "status":"foobar", + "updatecheck":{"status":"error-internal"} + } + ] + }})"; + +TEST(UpdateClientProtocolParserJSONTest, Parse) { + const auto parser = std::make_unique<ProtocolParserJSON>(); + + // Test parsing of a number of invalid JSON cases + EXPECT_FALSE(parser->Parse(std::string())); + EXPECT_FALSE(parser->errors().empty()); + + EXPECT_TRUE(parser->Parse(kJSONMissingAppId)); + EXPECT_TRUE(parser->results().list.empty()); + EXPECT_FALSE(parser->errors().empty()); + + EXPECT_TRUE(parser->Parse(kJSONInvalidCodebase)); + EXPECT_TRUE(parser->results().list.empty()); + EXPECT_FALSE(parser->errors().empty()); + + EXPECT_TRUE(parser->Parse(kJSONMissingVersion)); + EXPECT_TRUE(parser->results().list.empty()); + EXPECT_FALSE(parser->errors().empty()); + + EXPECT_TRUE(parser->Parse(kJSONInvalidVersion)); + EXPECT_TRUE(parser->results().list.empty()); + EXPECT_FALSE(parser->errors().empty()); + + EXPECT_TRUE(parser->Parse(kJSONInvalidMissingCodebase)); + EXPECT_TRUE(parser->results().list.empty()); + EXPECT_FALSE(parser->errors().empty()); + + EXPECT_TRUE(parser->Parse(kJSONInvalidMissingManifest)); + EXPECT_TRUE(parser->results().list.empty()); + EXPECT_FALSE(parser->errors().empty()); + + { + // Parse some valid XML, and check that all params came out as expected. + EXPECT_TRUE(parser->Parse(kJSONValid)); + EXPECT_TRUE(parser->errors().empty()); + EXPECT_EQ(1u, parser->results().list.size()); + const auto* first_result = &parser->results().list[0]; + EXPECT_STREQ("ok", first_result->status.c_str()); + EXPECT_EQ(1u, first_result->crx_urls.size()); + EXPECT_EQ(GURL("http://example.com/"), first_result->crx_urls[0]); + EXPECT_EQ(GURL("http://diff.example.com/"), first_result->crx_diffurls[0]); + EXPECT_EQ("1.2.3.4", first_result->manifest.version); + EXPECT_EQ("2.0.143.0", first_result->manifest.browser_min_version); + EXPECT_EQ(1u, first_result->manifest.packages.size()); + EXPECT_EQ("extension_1_2_3_4.crx", first_result->manifest.packages[0].name); + } + { + // Parse xml with hash value. + EXPECT_TRUE(parser->Parse(kJSONHash)); + EXPECT_TRUE(parser->errors().empty()); + EXPECT_FALSE(parser->results().list.empty()); + const auto* first_result = &parser->results().list[0]; + EXPECT_FALSE(first_result->manifest.packages.empty()); + EXPECT_EQ("1234", first_result->manifest.packages[0].hash_sha256); + EXPECT_EQ("5678", first_result->manifest.packages[0].hashdiff_sha256); + } + { + // Parse xml with package size value. + EXPECT_TRUE(parser->Parse(kJSONInvalidSizes)); + EXPECT_TRUE(parser->errors().empty()); + EXPECT_FALSE(parser->results().list.empty()); + const auto* first_result = &parser->results().list[0]; + EXPECT_FALSE(first_result->manifest.packages.empty()); + EXPECT_EQ(1234, first_result->manifest.packages[0].size); + EXPECT_EQ(9007199254740991, first_result->manifest.packages[1].size); + EXPECT_EQ(0, first_result->manifest.packages[2].size); + EXPECT_EQ(0, first_result->manifest.packages[3].size); + EXPECT_EQ(0, first_result->manifest.packages[4].size); + EXPECT_EQ(0, first_result->manifest.packages[5].size); + EXPECT_EQ(0, first_result->manifest.packages[6].size); + EXPECT_EQ(1234, first_result->manifest.packages[7].sizediff); + EXPECT_EQ(9007199254740991, first_result->manifest.packages[8].sizediff); + EXPECT_EQ(0, first_result->manifest.packages[9].sizediff); + EXPECT_EQ(0, first_result->manifest.packages[10].sizediff); + EXPECT_EQ(0, first_result->manifest.packages[11].sizediff); + EXPECT_EQ(0, first_result->manifest.packages[12].sizediff); + } + { + // Parse xml with a <daystart> element. + EXPECT_TRUE(parser->Parse(kJSONWithDaystart)); + EXPECT_TRUE(parser->errors().empty()); + EXPECT_FALSE(parser->results().list.empty()); + EXPECT_EQ(parser->results().daystart_elapsed_seconds, 456); + } + { + // Parse a no-update response. + EXPECT_TRUE(parser->Parse(kJSONNoUpdate)); + EXPECT_TRUE(parser->errors().empty()); + EXPECT_FALSE(parser->results().list.empty()); + const auto* first_result = &parser->results().list[0]; + EXPECT_STREQ("noupdate", first_result->status.c_str()); + EXPECT_EQ(first_result->extension_id, "12345"); + EXPECT_EQ(first_result->manifest.version, ""); + } + { + // Parse xml with one error and one success <app> tag. + EXPECT_TRUE(parser->Parse(kJSONTwoAppsOneError)); + EXPECT_TRUE(parser->errors().empty()); + EXPECT_EQ(2u, parser->results().list.size()); + const auto* first_result = &parser->results().list[0]; + EXPECT_EQ(first_result->extension_id, "aaaaaaaa"); + EXPECT_STREQ("error-unknownApplication", first_result->status.c_str()); + EXPECT_TRUE(first_result->manifest.version.empty()); + const auto* second_result = &parser->results().list[1]; + EXPECT_EQ(second_result->extension_id, "bbbbbbbb"); + EXPECT_STREQ("ok", second_result->status.c_str()); + EXPECT_EQ("1.2.3.4", second_result->manifest.version); + } + { + // Parse xml with two apps setting the cohort info. + EXPECT_TRUE(parser->Parse(kJSONTwoAppsSetCohort)); + EXPECT_TRUE(parser->errors().empty()); + EXPECT_EQ(2u, parser->results().list.size()); + const auto* first_result = &parser->results().list[0]; + EXPECT_EQ(first_result->extension_id, "aaaaaaaa"); + EXPECT_NE(first_result->cohort_attrs.find("cohort"), + first_result->cohort_attrs.end()); + EXPECT_EQ(first_result->cohort_attrs.find("cohort")->second, "1:2q3/"); + EXPECT_EQ(first_result->cohort_attrs.find("cohortname"), + first_result->cohort_attrs.end()); + EXPECT_EQ(first_result->cohort_attrs.find("cohorthint"), + first_result->cohort_attrs.end()); + const auto* second_result = &parser->results().list[1]; + EXPECT_EQ(second_result->extension_id, "bbbbbbbb"); + EXPECT_NE(second_result->cohort_attrs.find("cohort"), + second_result->cohort_attrs.end()); + EXPECT_EQ(second_result->cohort_attrs.find("cohort")->second, "1:33z@0.33"); + EXPECT_NE(second_result->cohort_attrs.find("cohortname"), + second_result->cohort_attrs.end()); + EXPECT_EQ(second_result->cohort_attrs.find("cohortname")->second, "cname"); + EXPECT_EQ(second_result->cohort_attrs.find("cohorthint"), + second_result->cohort_attrs.end()); + } + { + EXPECT_TRUE(parser->Parse(kJSONUpdateCheckStatusOkWithRunAction)); + EXPECT_TRUE(parser->errors().empty()); + EXPECT_FALSE(parser->results().list.empty()); + const auto* first_result = &parser->results().list[0]; + EXPECT_STREQ("ok", first_result->status.c_str()); + EXPECT_EQ(first_result->extension_id, "12345"); + EXPECT_STREQ("this", first_result->action_run.c_str()); + } + { + EXPECT_TRUE(parser->Parse(kJSONUpdateCheckStatusNoUpdateWithRunAction)); + EXPECT_TRUE(parser->errors().empty()); + EXPECT_FALSE(parser->results().list.empty()); + const auto* first_result = &parser->results().list[0]; + EXPECT_STREQ("noupdate", first_result->status.c_str()); + EXPECT_EQ(first_result->extension_id, "12345"); + EXPECT_STREQ("this", first_result->action_run.c_str()); + } + { + EXPECT_TRUE(parser->Parse(kJSONUpdateCheckStatusErrorWithRunAction)); + EXPECT_FALSE(parser->errors().empty()); + EXPECT_TRUE(parser->results().list.empty()); + } + { + EXPECT_TRUE(parser->Parse(kJSONAppsStatusError)); + EXPECT_STREQ("Unknown app status", parser->errors().c_str()); + EXPECT_EQ(3u, parser->results().list.size()); + const auto* first_result = &parser->results().list[0]; + EXPECT_EQ(first_result->extension_id, "aaaaaaaa"); + EXPECT_STREQ("error-unknownApplication", first_result->status.c_str()); + EXPECT_TRUE(first_result->manifest.version.empty()); + const auto* second_result = &parser->results().list[1]; + EXPECT_EQ(second_result->extension_id, "bbbbbbbb"); + EXPECT_STREQ("restricted", second_result->status.c_str()); + EXPECT_TRUE(second_result->manifest.version.empty()); + const auto* third_result = &parser->results().list[2]; + EXPECT_EQ(third_result->extension_id, "cccccccc"); + EXPECT_STREQ("error-invalidAppId", third_result->status.c_str()); + EXPECT_TRUE(third_result->manifest.version.empty()); + } +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/protocol_serializer.cc b/src/updater_components/update_client/protocol_serializer.cc new file mode 100644 index 0000000..5d106c5 --- /dev/null +++ b/src/updater_components/update_client/protocol_serializer.cc
@@ -0,0 +1,246 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/protocol_serializer.h" + +#include <utility> + +#include "base/containers/flat_map.h" +#include "base/guid.h" +#include "base/logging.h" +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/system/sys_info.h" +#include "base/version.h" +#include "build/build_config.h" +#include "components/update_client/activity_data_service.h" +#include "components/update_client/persisted_data.h" +#include "components/update_client/update_query_params.h" +#include "components/update_client/updater_state.h" + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + +namespace update_client { + +namespace { + +// Returns the amount of physical memory in GB, rounded to the nearest GB. +int GetPhysicalMemoryGB() { + const double kOneGB = 1024 * 1024 * 1024; + const int64_t phys_mem = base::SysInfo::AmountOfPhysicalMemory(); + return static_cast<int>(std::floor(0.5 + phys_mem / kOneGB)); +} + +std::string GetOSVersion() { +#if defined(OS_WIN) + const auto ver = base::win::OSInfo::GetInstance()->version_number(); + return base::StringPrintf("%d.%d.%d.%d", ver.major, ver.minor, ver.build, + ver.patch); +#else + return base::SysInfo().OperatingSystemVersion(); +#endif +} + +std::string GetServicePack() { +#if defined(OS_WIN) + return base::win::OSInfo::GetInstance()->service_pack_str(); +#else + return {}; +#endif +} + +} // namespace + +base::flat_map<std::string, std::string> BuildUpdateCheckExtraRequestHeaders( + const std::string& prod_id, + const base::Version& browser_version, + const std::vector<std::string>& ids, + bool is_foreground) { + // This number of extension ids results in an HTTP header length of about 1KB. + constexpr size_t maxIdsCount = 30; + const std::vector<std::string>& app_ids = + ids.size() <= maxIdsCount + ? ids + : std::vector<std::string>(ids.cbegin(), ids.cbegin() + maxIdsCount); + return { + {"X-Goog-Update-Updater", + base::StrCat({prod_id, "-", browser_version.GetString()})}, + {"X-Goog-Update-Interactivity", is_foreground ? "fg" : "bg"}, + {"X-Goog-Update-AppId", base::JoinString(app_ids, ",")}, + }; +} + +protocol_request::Request MakeProtocolRequest( + const std::string& session_id, + const std::string& prod_id, + const std::string& browser_version, + const std::string& lang, + const std::string& channel, + const std::string& os_long_name, + const std::string& download_preference, + const base::flat_map<std::string, std::string>& additional_attributes, + const std::map<std::string, std::string>* updater_state_attributes, + std::vector<protocol_request::App> apps) { + protocol_request::Request request; + request.protocol_version = kProtocolVersion; + + // Session id and request id. + DCHECK(!session_id.empty()); + DCHECK(base::StartsWith(session_id, "{", base::CompareCase::SENSITIVE)); + DCHECK(base::EndsWith(session_id, "}", base::CompareCase::SENSITIVE)); + request.session_id = session_id; + request.request_id = base::StrCat({"{", base::GenerateGUID(), "}"}); + + request.updatername = prod_id; + request.updaterversion = browser_version; + request.prodversion = browser_version; + request.lang = lang; + request.updaterchannel = channel; + request.prodchannel = channel; + request.operating_system = UpdateQueryParams::GetOS(); + request.arch = UpdateQueryParams::GetArch(); + request.nacl_arch = UpdateQueryParams::GetNaclArch(); + request.dlpref = download_preference; + request.additional_attributes = additional_attributes; + +#if defined(OS_WIN) + if (base::win::OSInfo::GetInstance()->wow64_status() == + base::win::OSInfo::WOW64_ENABLED) + request.is_wow64 = true; +#endif + + if (updater_state_attributes && + updater_state_attributes->count(UpdaterState::kIsEnterpriseManaged)) { + request.domain_joined = + updater_state_attributes->at(UpdaterState::kIsEnterpriseManaged) == "1"; + } + + // HW platform information. + request.hw.physmemory = GetPhysicalMemoryGB(); + + // OS version and platform information. + request.os.platform = os_long_name; + request.os.version = GetOSVersion(); + request.os.service_pack = GetServicePack(); + request.os.arch = base::SysInfo().OperatingSystemArchitecture(); + + if (updater_state_attributes) { + request.updater = base::make_optional<protocol_request::Updater>(); + auto it = updater_state_attributes->find("name"); + if (it != updater_state_attributes->end()) + request.updater->name = it->second; + it = updater_state_attributes->find("version"); + if (it != updater_state_attributes->end()) + request.updater->version = it->second; + it = updater_state_attributes->find("ismachine"); + if (it != updater_state_attributes->end()) { + DCHECK(it->second == "0" || it->second == "1"); + request.updater->is_machine = it->second != "0"; + } + it = updater_state_attributes->find("autoupdatecheckenabled"); + if (it != updater_state_attributes->end()) { + DCHECK(it->second == "0" || it->second == "1"); + request.updater->autoupdate_check_enabled = it->second != "0"; + } + it = updater_state_attributes->find("laststarted"); + if (it != updater_state_attributes->end()) { + int last_started = 0; + if (base::StringToInt(it->second, &last_started)) + request.updater->last_started = last_started; + } + it = updater_state_attributes->find("lastchecked"); + if (it != updater_state_attributes->end()) { + int last_checked = 0; + if (base::StringToInt(it->second, &last_checked)) + request.updater->last_checked = last_checked; + } + it = updater_state_attributes->find("updatepolicy"); + if (it != updater_state_attributes->end()) { + int update_policy = 0; + if (base::StringToInt(it->second, &update_policy)) + request.updater->update_policy = update_policy; + } + } + + request.apps = std::move(apps); + return request; +} + +protocol_request::App MakeProtocolApp( + const std::string& app_id, + const base::Version& version, + base::Optional<std::vector<base::Value>> events) { + protocol_request::App app; + app.app_id = app_id; + app.version = version.GetString(); + app.events = std::move(events); + return app; +} + +protocol_request::App MakeProtocolApp( + const std::string& app_id, + const base::Version& version, + const std::string& brand_code, + const std::string& install_source, + const std::string& install_location, + const std::string& fingerprint, + const base::flat_map<std::string, std::string>& installer_attributes, + const std::string& cohort, + const std::string& cohort_hint, + const std::string& cohort_name, + const std::vector<int>& disabled_reasons, + base::Optional<protocol_request::UpdateCheck> update_check, + base::Optional<protocol_request::Ping> ping) { + auto app = MakeProtocolApp(app_id, version, base::nullopt); + app.brand_code = brand_code; + app.install_source = install_source; + app.install_location = install_location; + app.fingerprint = fingerprint; + app.installer_attributes = installer_attributes; + app.cohort = cohort; + app.cohort_hint = cohort_hint; + app.cohort_name = cohort_name; + app.enabled = disabled_reasons.empty(); + app.disabled_reasons = disabled_reasons; + app.update_check = std::move(update_check); + app.ping = std::move(ping); + return app; +} + +protocol_request::UpdateCheck MakeProtocolUpdateCheck(bool is_update_disabled) { + protocol_request::UpdateCheck update_check; + update_check.is_update_disabled = is_update_disabled; + return update_check; +} + +protocol_request::Ping MakeProtocolPing(const std::string& app_id, + const PersistedData* metadata) { + DCHECK(metadata); + protocol_request::Ping ping; + + if (metadata->GetActiveBit(app_id)) { + const int date_last_active = metadata->GetDateLastActive(app_id); + if (date_last_active != kDateUnknown) { + ping.date_last_active = date_last_active; + } else { + ping.days_since_last_active_ping = + metadata->GetDaysSinceLastActive(app_id); + } + } + const int date_last_roll_call = metadata->GetDateLastRollCall(app_id); + if (date_last_roll_call != kDateUnknown) { + ping.date_last_roll_call = date_last_roll_call; + } else { + ping.days_since_last_roll_call = metadata->GetDaysSinceLastRollCall(app_id); + } + ping.ping_freshness = metadata->GetPingFreshness(app_id); + + return ping; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/protocol_serializer.h b/src/updater_components/update_client/protocol_serializer.h new file mode 100644 index 0000000..47c212a --- /dev/null +++ b/src/updater_components/update_client/protocol_serializer.h
@@ -0,0 +1,84 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_H_ +#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_H_ + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/containers/flat_map.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/update_client/protocol_definition.h" + +namespace base { +class Version; +} + +namespace update_client { + +class PersistedData; + +// Creates the values for the DDOS extra request headers sent with the update +// check. These headers include "X-Goog-Update-Updater", +// "X-Goog-Update-AppId", and "X-Goog-Update-Interactivity". +base::flat_map<std::string, std::string> BuildUpdateCheckExtraRequestHeaders( + const std::string& prod_id, + const base::Version& browser_version, + const std::vector<std::string>& ids_checked, + bool is_foreground); + +protocol_request::Request MakeProtocolRequest( + const std::string& session_id, + const std::string& prod_id, + const std::string& browser_version, + const std::string& lang, + const std::string& channel, + const std::string& os_long_name, + const std::string& download_preference, + const base::flat_map<std::string, std::string>& additional_attributes, + const std::map<std::string, std::string>* updater_state_attributes, + std::vector<protocol_request::App> apps); + +protocol_request::App MakeProtocolApp( + const std::string& app_id, + const base::Version& version, + base::Optional<std::vector<base::Value>> events); + +protocol_request::App MakeProtocolApp( + const std::string& app_id, + const base::Version& version, + const std::string& brand_code, + const std::string& install_source, + const std::string& install_location, + const std::string& fingerprint, + const base::flat_map<std::string, std::string>& installer_attributes, + const std::string& cohort, + const std::string& cohort_hint, + const std::string& cohort_name, + const std::vector<int>& disabled_reasons, + base::Optional<protocol_request::UpdateCheck> update_check, + base::Optional<protocol_request::Ping> ping); + +protocol_request::UpdateCheck MakeProtocolUpdateCheck(bool is_update_disabled); + +protocol_request::Ping MakeProtocolPing(const std::string& app_id, + const PersistedData* metadata); + +class ProtocolSerializer { + public: + virtual ~ProtocolSerializer() = default; + virtual std::string Serialize( + const protocol_request::Request& request) const = 0; + + protected: + ProtocolSerializer() = default; +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_H_
diff --git a/src/updater_components/update_client/protocol_serializer_json.cc b/src/updater_components/update_client/protocol_serializer_json.cc new file mode 100644 index 0000000..8e7aee8 --- /dev/null +++ b/src/updater_components/update_client/protocol_serializer_json.cc
@@ -0,0 +1,183 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/protocol_serializer_json.h" + +#include <memory> +#include <utility> +#include <vector> + +#include "base/json/json_writer.h" +#include "base/values.h" +#include "build/branding_buildflags.h" +#include "build/build_config.h" +#include "components/update_client/updater_state.h" + +namespace update_client { + +using Value = base::Value; + +std::string ProtocolSerializerJSON::Serialize( + const protocol_request::Request& request) const { + Value root_node(Value::Type::DICTIONARY); + auto* request_node = + root_node.SetKey("request", Value(Value::Type::DICTIONARY)); + request_node->SetKey("protocol", Value(request.protocol_version)); + request_node->SetKey("dedup", Value("cr")); + request_node->SetKey("acceptformat", Value("crx2,crx3")); + if (!request.additional_attributes.empty()) { + for (const auto& attr : request.additional_attributes) + request_node->SetKey(attr.first, Value(attr.second)); + } + request_node->SetKey("sessionid", Value(request.session_id)); + request_node->SetKey("requestid", Value(request.request_id)); + request_node->SetKey("@updater", Value(request.updatername)); + request_node->SetKey("prodversion", Value(request.updaterversion)); + request_node->SetKey("updaterversion", Value(request.prodversion)); + request_node->SetKey("lang", Value(request.lang)); + request_node->SetKey("@os", Value(request.operating_system)); + request_node->SetKey("arch", Value(request.arch)); + request_node->SetKey("nacl_arch", Value(request.nacl_arch)); +#if defined(OS_WIN) + if (request.is_wow64) + request_node->SetKey("wow64", Value(request.is_wow64)); +#endif // OS_WIN + if (!request.updaterchannel.empty()) + request_node->SetKey("updaterchannel", Value(request.updaterchannel)); + if (!request.prodchannel.empty()) + request_node->SetKey("prodchannel", Value(request.prodchannel)); + if (!request.dlpref.empty()) + request_node->SetKey("dlpref", Value(request.dlpref)); + if (request.domain_joined) { + request_node->SetKey(UpdaterState::kIsEnterpriseManaged, + Value(*request.domain_joined)); + } + + // HW platform information. + auto* hw_node = request_node->SetKey("hw", Value(Value::Type::DICTIONARY)); + hw_node->SetKey("physmemory", Value(static_cast<int>(request.hw.physmemory))); + + // OS version and platform information. + auto* os_node = request_node->SetKey("os", Value(Value::Type::DICTIONARY)); + os_node->SetKey("platform", Value(request.os.platform)); + os_node->SetKey("arch", Value(request.os.arch)); + if (!request.os.version.empty()) + os_node->SetKey("version", Value(request.os.version)); + if (!request.os.service_pack.empty()) + os_node->SetKey("sp", Value(request.os.service_pack)); + +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + if (request.updater) { + const auto& updater = *request.updater; + auto* updater_node = + request_node->SetKey("updater", Value(Value::Type::DICTIONARY)); + updater_node->SetKey("name", Value(updater.name)); + updater_node->SetKey("ismachine", Value(updater.is_machine)); + updater_node->SetKey("autoupdatecheckenabled", + Value(updater.autoupdate_check_enabled)); + updater_node->SetKey("updatepolicy", Value(updater.update_policy)); + if (!updater.version.empty()) + updater_node->SetKey("version", Value(updater.version)); + if (updater.last_checked) + updater_node->SetKey("lastchecked", Value(*updater.last_checked)); + if (updater.last_started) + updater_node->SetKey("laststarted", Value(*updater.last_started)); + } +#endif + + std::vector<Value> app_nodes; + for (const auto& app : request.apps) { + Value app_node(Value::Type::DICTIONARY); + app_node.SetKey("appid", Value(app.app_id)); + app_node.SetKey("version", Value(app.version)); + if (!app.brand_code.empty()) + app_node.SetKey("brand", Value(app.brand_code)); + if (!app.install_source.empty()) + app_node.SetKey("installsource", Value(app.install_source)); + if (!app.install_location.empty()) + app_node.SetKey("installedby", Value(app.install_location)); + if (!app.cohort.empty()) + app_node.SetKey("cohort", Value(app.cohort)); + if (!app.cohort_name.empty()) + app_node.SetKey("cohortname", Value(app.cohort_name)); + if (!app.cohort_hint.empty()) + app_node.SetKey("cohorthint", Value(app.cohort_hint)); + if (app.enabled) + app_node.SetKey("enabled", Value(*app.enabled)); + + if (app.disabled_reasons && !app.disabled_reasons->empty()) { + std::vector<Value> disabled_nodes; + for (const int disabled_reason : *app.disabled_reasons) { + Value disabled_node(Value::Type::DICTIONARY); + disabled_node.SetKey("reason", Value(disabled_reason)); + disabled_nodes.push_back(std::move(disabled_node)); + } + app_node.SetKey("disabled", Value(disabled_nodes)); + } + + for (const auto& attr : app.installer_attributes) + app_node.SetKey(attr.first, Value(attr.second)); + + if (app.update_check) { + auto* update_check_node = + app_node.SetKey("updatecheck", Value(Value::Type::DICTIONARY)); + if (app.update_check->is_update_disabled) + update_check_node->SetKey("updatedisabled", Value(true)); + } + + if (app.ping) { + const auto& ping = *app.ping; + auto* ping_node = app_node.SetKey("ping", Value(Value::Type::DICTIONARY)); + if (!ping.ping_freshness.empty()) + ping_node->SetKey("ping_freshness", Value(ping.ping_freshness)); + + // Output "ad" or "a" only if the this app has been seen 'active'. + if (ping.date_last_active) { + ping_node->SetKey("ad", Value(*ping.date_last_active)); + } else if (ping.days_since_last_active_ping) { + ping_node->SetKey("a", Value(*ping.days_since_last_active_ping)); + } + + // Output "rd" if valid or "r" as a last resort roll call metric. + if (ping.date_last_roll_call) + ping_node->SetKey("rd", Value(*ping.date_last_roll_call)); + else + ping_node->SetKey("r", Value(ping.days_since_last_roll_call)); + } + + if (!app.fingerprint.empty()) { + std::vector<Value> package_nodes; + Value package(Value::Type::DICTIONARY); + package.SetKey("fp", Value(app.fingerprint)); + package_nodes.push_back(std::move(package)); + auto* packages_node = + app_node.SetKey("packages", Value(Value::Type::DICTIONARY)); + packages_node->SetKey("package", Value(package_nodes)); + } + + if (app.events) { + std::vector<Value> event_nodes; + for (const auto& event : *app.events) { + DCHECK(event.is_dict()); + DCHECK(!event.DictEmpty()); + event_nodes.push_back(event.Clone()); + } + app_node.SetKey("event", Value(event_nodes)); + } + + app_nodes.push_back(std::move(app_node)); + } + + if (!app_nodes.empty()) + request_node->SetKey("app", Value(std::move(app_nodes))); + + std::string msg; + return base::JSONWriter::WriteWithOptions( + root_node, base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION, + &msg) + ? msg + : std::string(); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/protocol_serializer_json.h b/src/updater_components/update_client/protocol_serializer_json.h new file mode 100644 index 0000000..71f2a99 --- /dev/null +++ b/src/updater_components/update_client/protocol_serializer_json.h
@@ -0,0 +1,28 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_JSON_H_ +#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_JSON_H_ + +#include <string> + +#include "components/update_client/protocol_serializer.h" + +namespace update_client { + +class ProtocolSerializerJSON final : public ProtocolSerializer { + public: + ProtocolSerializerJSON() = default; + + // Overrides for ProtocolSerializer. + std::string Serialize( + const protocol_request::Request& request) const override; + + private: + DISALLOW_COPY_AND_ASSIGN(ProtocolSerializerJSON); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_JSON_H_
diff --git a/src/updater_components/update_client/protocol_serializer_json_unittest.cc b/src/updater_components/update_client/protocol_serializer_json_unittest.cc new file mode 100644 index 0000000..4cf2666 --- /dev/null +++ b/src/updater_components/update_client/protocol_serializer_json_unittest.cc
@@ -0,0 +1,128 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/protocol_serializer_json.h" + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/optional.h" +#include "base/values.h" +#include "base/version.h" +#include "build/branding_buildflags.h" +#include "components/prefs/testing_pref_service.h" +#include "components/update_client/activity_data_service.h" +#include "components/update_client/persisted_data.h" +#include "components/update_client/protocol_definition.h" +#include "components/update_client/protocol_serializer.h" +#include "components/update_client/updater_state.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/re2/src/re2/re2.h" + +using base::Value; +using std::string; + +namespace update_client { + +TEST(SerializeRequestJSON, Serialize) { + // When no updater state is provided, then check that the elements and + // attributes related to the updater state are not serialized. + + std::vector<base::Value> events; + events.push_back(Value(Value::Type::DICTIONARY)); + events.push_back(Value(Value::Type::DICTIONARY)); + events[0].SetKey("a", Value(1)); + events[0].SetKey("b", Value("2")); + events[1].SetKey("error", Value(0)); + + auto pref = std::make_unique<TestingPrefServiceSimple>(); + PersistedData::RegisterPrefs(pref->registry()); + auto metadata = std::make_unique<PersistedData>(pref.get(), nullptr); + std::vector<std::string> items = {"id1"}; + metadata->SetDateLastRollCall(items, 1234); + + std::vector<protocol_request::App> apps; + apps.push_back(MakeProtocolApp( + "id1", base::Version("1.0"), "brand1", "source1", "location1", "fp1", + {{"attr1", "1"}, {"attr2", "2"}}, "c1", "ch1", "cn1", {0, 1}, + MakeProtocolUpdateCheck(true), MakeProtocolPing("id1", metadata.get()))); + apps.push_back( + MakeProtocolApp("id2", base::Version("2.0"), std::move(events))); + + const auto request = std::make_unique<ProtocolSerializerJSON>()->Serialize( + MakeProtocolRequest("{15160585-8ADE-4D3C-839B-1281A6035D1F}", "prod_id", + "1.0", "lang", "channel", "OS", "cacheable", + {{"extra", "params"}}, nullptr, std::move(apps))); + constexpr char regex[] = + R"({"request":{"@os":"\w+","@updater":"prod_id",)" + R"("acceptformat":"crx2,crx3",)" + R"("app":\[{"appid":"id1","attr1":"1","attr2":"2","brand":"brand1",)" + R"("cohort":"c1","cohorthint":"ch1","cohortname":"cn1",)" + R"("disabled":\[{"reason":0},{"reason":1}],"enabled":false,)" + R"("installedby":"location1","installsource":"source1",)" + R"("packages":{"package":\[{"fp":"fp1"}]},)" + R"("ping":{"ping_freshness":"{[-\w]{36}}","rd":1234},)" + R"("updatecheck":{"updatedisabled":true},"version":"1.0"},)" + R"({"appid":"id2","event":\[{"a":1,"b":"2"},{"error":0}],)" + R"("version":"2.0"}],"arch":"\w+","dedup":"cr","dlpref":"cacheable",)" + R"("extra":"params","hw":{"physmemory":\d+},"lang":"lang",)" + R"("nacl_arch":"[-\w]+","os":{"arch":"[_,-.\w]+","platform":"OS",)" + R"(("sp":"[\s\w]+",)?"version":"[-.\w]+"},"prodchannel":"channel",)" + R"("prodversion":"1.0","protocol":"3.1","requestid":"{[-\w]{36}}",)" + R"("sessionid":"{[-\w]{36}}","updaterchannel":"channel",)" + R"("updaterversion":"1.0"(,"wow64":true)?}})"; + EXPECT_TRUE(RE2::FullMatch(request, regex)) << request; +} + +TEST(SerializeRequestJSON, DownloadPreference) { + // Verifies that an empty |download_preference| is not serialized. + const auto serializer = std::make_unique<ProtocolSerializerJSON>(); + auto request = serializer->Serialize( + MakeProtocolRequest("{15160585-8ADE-4D3C-839B-1281A6035D1F}", "", "", "", + "", "", "", {}, nullptr, {})); + EXPECT_FALSE(RE2::PartialMatch(request, R"("dlpref":)")) << request; + + // Verifies that |download_preference| is serialized. + request = serializer->Serialize( + MakeProtocolRequest("{15160585-8ADE-4D3C-839B-1281A6035D1F}", "", "", "", + "", "", "cacheable", {}, nullptr, {})); + EXPECT_TRUE(RE2::PartialMatch(request, R"("dlpref":"cacheable")")) << request; +} + +// When present, updater state attributes are only serialized for Google builds, +// except the |domainjoined| attribute, which is serialized in all cases. +TEST(SerializeRequestJSON, UpdaterStateAttributes) { + const auto serializer = std::make_unique<ProtocolSerializerJSON>(); + UpdaterState::Attributes attributes; + attributes["ismachine"] = "1"; + attributes["domainjoined"] = "1"; + attributes["name"] = "Omaha"; + attributes["version"] = "1.2.3.4"; + attributes["laststarted"] = "1"; + attributes["lastchecked"] = "2"; + attributes["autoupdatecheckenabled"] = "0"; + attributes["updatepolicy"] = "-1"; + const auto request = serializer->Serialize(MakeProtocolRequest( + "{15160585-8ADE-4D3C-839B-1281A6035D1F}", "prod_id", "1.0", "lang", + "channel", "OS", "cacheable", {{"extra", "params"}}, &attributes, {})); + constexpr char regex[] = + R"({"request":{"@os":"\w+","@updater":"prod_id",)" + R"("acceptformat":"crx2,crx3","arch":"\w+","dedup":"cr",)" + R"("dlpref":"cacheable","domainjoined":true,"extra":"params",)" + R"("hw":{"physmemory":\d+},"lang":"lang","nacl_arch":"[-\w]+",)" + R"("os":{"arch":"[,-.\w]+","platform":"OS",("sp":"[\s\w]+",)?)" + R"("version":"[-.\w]+"},"prodchannel":"channel","prodversion":"1.0",)" + R"("protocol":"3.1","requestid":"{[-\w]{36}}","sessionid":"{[-\w]{36}}",)" +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + R"("updater":{"autoupdatecheckenabled":false,"ismachine":true,)" + R"("lastchecked":2,"laststarted":1,"name":"Omaha","updatepolicy":-1,)" + R"("version":"1\.2\.3\.4"},)" +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) + R"("updaterchannel":"channel","updaterversion":"1.0"(,"wow64":true)?}})"; + EXPECT_TRUE(RE2::FullMatch(request, regex)) << request; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/protocol_serializer_unittest.cc b/src/updater_components/update_client/protocol_serializer_unittest.cc new file mode 100644 index 0000000..f0c8a14 --- /dev/null +++ b/src/updater_components/update_client/protocol_serializer_unittest.cc
@@ -0,0 +1,55 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/strings/string_util.h" +#include "base/version.h" +#include "components/update_client/protocol_serializer.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace update_client { + +TEST(BuildProtocolRequest, BuildUpdateCheckExtraRequestHeaders) { + auto headers = BuildUpdateCheckExtraRequestHeaders( + "fake_prodid", base::Version("30.0"), {}, true); + EXPECT_EQ("fake_prodid-30.0", headers["X-Goog-Update-Updater"]); + EXPECT_EQ("fg", headers["X-Goog-Update-Interactivity"]); + EXPECT_TRUE(headers["X-Goog-Update-AppId"].empty()); + + headers = BuildUpdateCheckExtraRequestHeaders( + "fake_prodid", base::Version("30.0"), {}, false); + EXPECT_EQ("fake_prodid-30.0", headers["X-Goog-Update-Updater"]); + EXPECT_EQ("bg", headers["X-Goog-Update-Interactivity"]); + EXPECT_TRUE(headers["X-Goog-Update-AppId"].empty()); + + headers = BuildUpdateCheckExtraRequestHeaders( + "fake_prodid", base::Version("30.0"), + {"jebgalgnebhfojomionfpkfelancnnkf"}, true); + EXPECT_EQ("fake_prodid-30.0", headers["X-Goog-Update-Updater"]); + EXPECT_EQ("fg", headers["X-Goog-Update-Interactivity"]); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", headers["X-Goog-Update-AppId"]); + + headers = BuildUpdateCheckExtraRequestHeaders( + "fake_prodid", base::Version("30.0"), + {"jebgalgnebhfojomionfpkfelancnnkf", "ihfokbkgjpifbbojhneepfflplebdkc"}, + true); + EXPECT_EQ("fake_prodid-30.0", headers["X-Goog-Update-Updater"]); + EXPECT_EQ("fg", headers["X-Goog-Update-Interactivity"]); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf,ihfokbkgjpifbbojhneepfflplebdkc", + headers["X-Goog-Update-AppId"]); + + // Test that only 30 extension ids are joined in the headers. + headers = BuildUpdateCheckExtraRequestHeaders( + "fake_prodid", base::Version("30.0"), + std::vector<std::string>(40, "jebgalgnebhfojomionfpkfelancnnkf"), true); + EXPECT_EQ(base::JoinString(std::vector<std::string>( + 30, "jebgalgnebhfojomionfpkfelancnnkf"), + ","), + headers["X-Goog-Update-AppId"]); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/request_sender.cc b/src/updater_components/update_client/request_sender.cc new file mode 100644 index 0000000..a68df98 --- /dev/null +++ b/src/updater_components/update_client/request_sender.cc
@@ -0,0 +1,204 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/request_sender.h" + +#include <utility> + +#include "base/base64.h" +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/client_update_protocol/ecdsa.h" +#include "components/update_client/configurator.h" +#include "components/update_client/network.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/utils.h" + +namespace update_client { + +namespace { + +// This is an ECDSA prime256v1 named-curve key. +constexpr int kKeyVersion = 9; +const char kKeyPubBytesBase64[] = + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsVwVMmIJaWBjktSx9m1JrZWYBvMm" + "bsrGGQPhScDtao+DloD871YmEeunAaQvRMZgDh1nCaWkVG6wo75+yDbKDA=="; + +} // namespace + +RequestSender::RequestSender(scoped_refptr<Configurator> config) + : config_(config) {} + +RequestSender::~RequestSender() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void RequestSender::Send( + const std::vector<GURL>& urls, + const base::flat_map<std::string, std::string>& request_extra_headers, + const std::string& request_body, + bool use_signing, + RequestSenderCallback request_sender_callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + urls_ = urls; + request_extra_headers_ = request_extra_headers; + request_body_ = request_body; + use_signing_ = use_signing; + request_sender_callback_ = std::move(request_sender_callback); + + if (urls_.empty()) { + return HandleSendError(static_cast<int>(ProtocolError::MISSING_URLS), 0); + } + + cur_url_ = urls_.begin(); + + if (use_signing_) { + public_key_ = GetKey(kKeyPubBytesBase64); + if (public_key_.empty()) + return HandleSendError( + static_cast<int>(ProtocolError::MISSING_PUBLIC_KEY), 0); + } + + SendInternal(); +} + +void RequestSender::SendInternal() { + DCHECK(cur_url_ != urls_.end()); + DCHECK(cur_url_->is_valid()); + DCHECK(thread_checker_.CalledOnValidThread()); + + GURL url(*cur_url_); + + if (use_signing_) { + DCHECK(!public_key_.empty()); + signer_ = client_update_protocol::Ecdsa::Create(kKeyVersion, public_key_); + std::string request_query_string; + signer_->SignRequest(request_body_, &request_query_string); + + url = BuildUpdateUrl(url, request_query_string); + } + + network_fetcher_ = config_->GetNetworkFetcherFactory()->Create(); + if (!network_fetcher_) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&RequestSender::SendInternalComplete, + base::Unretained(this), + static_cast<int>(ProtocolError::URL_FETCHER_FAILED), + std::string(), std::string(), 0)); + } + network_fetcher_->PostRequest( + url, request_body_, request_extra_headers_, + base::BindOnce(&RequestSender::OnResponseStarted, base::Unretained(this)), + base::BindRepeating([](int64_t current) {}), + base::BindOnce(&RequestSender::OnNetworkFetcherComplete, + base::Unretained(this), url)); +} + +void RequestSender::SendInternalComplete(int error, + const std::string& response_body, + const std::string& response_etag, + int retry_after_sec) { + if (!error) { + if (!use_signing_) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(request_sender_callback_), 0, + response_body, retry_after_sec)); + return; + } + + DCHECK(use_signing_); + DCHECK(signer_); + if (signer_->ValidateResponse(response_body, response_etag)) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(request_sender_callback_), 0, + response_body, retry_after_sec)); + return; + } + + error = static_cast<int>(ProtocolError::RESPONSE_NOT_TRUSTED); + } + + DCHECK(error); + + // A positive |retry_after_sec| is a hint from the server that the client + // should not send further request until the cooldown has expired. + if (retry_after_sec <= 0 && ++cur_url_ != urls_.end() && + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&RequestSender::SendInternal, + base::Unretained(this)))) { + return; + } + + HandleSendError(error, retry_after_sec); +} + +void RequestSender::OnResponseStarted(const GURL& final_url, + int response_code, + int64_t content_length) { + response_code_ = response_code; +} + +void RequestSender::OnNetworkFetcherComplete( + const GURL& original_url, + std::unique_ptr<std::string> response_body, + int net_error, + const std::string& header_etag, + int64_t xheader_retry_after_sec) { + DCHECK(thread_checker_.CalledOnValidThread()); + + VLOG(1) << "request completed from url: " << original_url.spec(); + + int error = -1; + if (response_body && response_code_ == 200) { + DCHECK_EQ(0, net_error); + error = 0; + } else if (response_code_ != -1) { + error = response_code_; + } else { + error = net_error; + } + + int retry_after_sec = -1; + if (original_url.SchemeIsCryptographic() && error > 0) + retry_after_sec = base::saturated_cast<int>(xheader_retry_after_sec); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&RequestSender::SendInternalComplete, + base::Unretained(this), error, + response_body ? *response_body : std::string(), + header_etag, retry_after_sec)); +} + +void RequestSender::HandleSendError(int error, int retry_after_sec) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(request_sender_callback_), error, + std::string(), retry_after_sec)); +} + +std::string RequestSender::GetKey(const char* key_bytes_base64) { + std::string result; + return base::Base64Decode(std::string(key_bytes_base64), &result) + ? result + : std::string(); +} + +GURL RequestSender::BuildUpdateUrl(const GURL& url, + const std::string& query_params) { + const std::string query_string( + url.has_query() ? base::StringPrintf("%s&%s", url.query().c_str(), + query_params.c_str()) + : query_params); + GURL::Replacements replacements; + replacements.SetQueryStr(query_string); + + return url.ReplaceComponents(replacements); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/request_sender.h b/src/updater_components/update_client/request_sender.h new file mode 100644 index 0000000..7b9e21b --- /dev/null +++ b/src/updater_components/update_client/request_sender.h
@@ -0,0 +1,113 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_REQUEST_SENDER_H_ +#define COMPONENTS_UPDATE_CLIENT_REQUEST_SENDER_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "url/gurl.h" + +namespace client_update_protocol { +class Ecdsa; +} + +namespace update_client { + +class Configurator; +class NetworkFetcher; + +// Sends a request to one of the urls provided. The class implements a chain +// of responsibility design pattern, where the urls are tried in the order they +// are specified, until the request to one of them succeeds or all have failed. +// CUP signing is optional. +class RequestSender { + public: + // If |error| is 0, then the response is provided in the |response| parameter. + // |retry_after_sec| contains the value of the X-Retry-After response header, + // when the response was received from a cryptographically secure URL. The + // range for this value is [-1, 86400]. If |retry_after_sec| is -1 it means + // that the header could not be found, or trusted, or had an invalid value. + // The upper bound represents a delay of one day. + using RequestSenderCallback = base::OnceCallback< + void(int error, const std::string& response, int retry_after_sec)>; + + explicit RequestSender(scoped_refptr<Configurator> config); + ~RequestSender(); + + // |use_signing| enables CUP signing of protocol messages exchanged using + // this class. |is_foreground| controls the presence and the value for the + // X-GoogleUpdate-Interactvity header serialized in the protocol request. + // If this optional parameter is set, the values of "fg" or "bg" are sent + // for true or false values of this parameter. Otherwise the header is not + // sent at all. + void Send( + const std::vector<GURL>& urls, + const base::flat_map<std::string, std::string>& request_extra_headers, + const std::string& request_body, + bool use_signing, + RequestSenderCallback request_sender_callback); + + private: + // Combines the |url| and |query_params| parameters. + static GURL BuildUpdateUrl(const GURL& url, const std::string& query_params); + + // Decodes and returns the public key used by CUP. + static std::string GetKey(const char* key_bytes_base64); + + void OnResponseStarted(const GURL& final_url, + int response_code, + int64_t content_length); + + void OnNetworkFetcherComplete(const GURL& original_url, + std::unique_ptr<std::string> response_body, + int net_error, + const std::string& header_etag, + int64_t xheader_retry_after_sec); + + // Implements the error handling and url fallback mechanism. + void SendInternal(); + + // Called when SendInternal completes. |response_body| and |response_etag| + // contain the body and the etag associated with the HTTP response. + void SendInternalComplete(int error, + const std::string& response_body, + const std::string& response_etag, + int retry_after_sec); + + // Helper function to handle a non-continuable error in Send. + void HandleSendError(int error, int retry_after_sec); + + base::ThreadChecker thread_checker_; + + const scoped_refptr<Configurator> config_; + + std::vector<GURL> urls_; + base::flat_map<std::string, std::string> request_extra_headers_; + std::string request_body_; + bool use_signing_ = false; // True if CUP signing is used. + RequestSenderCallback request_sender_callback_; + + std::string public_key_; + std::vector<GURL>::const_iterator cur_url_; + std::unique_ptr<NetworkFetcher> network_fetcher_; + std::unique_ptr<client_update_protocol::Ecdsa> signer_; + + int response_code_ = -1; + + DISALLOW_COPY_AND_ASSIGN(RequestSender); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_REQUEST_SENDER_H_
diff --git a/src/updater_components/update_client/request_sender_unittest.cc b/src/updater_components/update_client/request_sender_unittest.cc new file mode 100644 index 0000000..cb0e09c --- /dev/null +++ b/src/updater_components/update_client/request_sender_unittest.cc
@@ -0,0 +1,261 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/request_sender.h" + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/strings/string_util.h" +#include "base/test/task_environment.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/update_client/net/url_loader_post_interceptor.h" +#include "components/update_client/test_configurator.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace update_client { + +namespace { + +const char kUrl1[] = "https://localhost2/path1"; +const char kUrl2[] = "https://localhost2/path2"; + +// TODO(sorin): refactor as a utility function for unit tests. +base::FilePath test_file(const char* file) { + base::FilePath path; + base::PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("components") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("update_client") + .AppendASCII(file); +} + +} // namespace + +class RequestSenderTest : public testing::Test, + public ::testing::WithParamInterface<bool> { + public: + RequestSenderTest(); + ~RequestSenderTest() override; + + // Overrides from testing::Test. + void SetUp() override; + void TearDown() override; + + void RequestSenderComplete(int error, + const std::string& response, + int retry_after_sec); + + protected: + void Quit(); + void RunThreads(); + + base::test::TaskEnvironment task_environment_; + + scoped_refptr<TestConfigurator> config_; + std::unique_ptr<RequestSender> request_sender_; + + std::unique_ptr<URLLoaderPostInterceptor> post_interceptor_; + + int error_ = 0; + std::string response_; + + private: + base::OnceClosure quit_closure_; + + DISALLOW_COPY_AND_ASSIGN(RequestSenderTest); +}; + +INSTANTIATE_TEST_SUITE_P(IsForeground, RequestSenderTest, ::testing::Bool()); + +RequestSenderTest::RequestSenderTest() + : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {} + +RequestSenderTest::~RequestSenderTest() {} + +void RequestSenderTest::SetUp() { + config_ = base::MakeRefCounted<TestConfigurator>(); + request_sender_ = std::make_unique<RequestSender>(config_); + + std::vector<GURL> urls; + urls.push_back(GURL(kUrl1)); + urls.push_back(GURL(kUrl2)); + + post_interceptor_ = std::make_unique<URLLoaderPostInterceptor>( + urls, config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor_); +} + +void RequestSenderTest::TearDown() { + request_sender_ = nullptr; + + post_interceptor_.reset(); + + // Run the threads until they are idle to allow the clean up + // of the network interceptors on the IO thread. + task_environment_.RunUntilIdle(); + config_ = nullptr; +} + +void RequestSenderTest::RunThreads() { + base::RunLoop runloop; + quit_closure_ = runloop.QuitClosure(); + runloop.Run(); +} + +void RequestSenderTest::Quit() { + if (!quit_closure_.is_null()) + std::move(quit_closure_).Run(); +} + +void RequestSenderTest::RequestSenderComplete(int error, + const std::string& response, + int retry_after_sec) { + error_ = error; + response_ = response; + + Quit(); +} + +// Tests that when a request to the first url succeeds, the subsequent urls are +// not tried. +TEST_P(RequestSenderTest, RequestSendSuccess) { + EXPECT_TRUE( + post_interceptor_->ExpectRequest(std::make_unique<PartialMatch>("test"), + test_file("updatecheck_reply_1.json"))); + + const bool is_foreground = GetParam(); + request_sender_->Send( + {GURL(kUrl1), GURL(kUrl2)}, + {{"X-Goog-Update-Interactivity", is_foreground ? "fg" : "bg"}}, "test", + false, + base::BindOnce(&RequestSenderTest::RequestSenderComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(1, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + EXPECT_EQ(0, post_interceptor_->GetHitCountForURL(GURL(kUrl2))) + << post_interceptor_->GetRequestsAsString(); + + // Sanity check the request. + EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str()); + + // Check the response post conditions. + EXPECT_EQ(0, error_); + EXPECT_EQ(419ul, response_.size()); + + // Check the interactivity header value. + const auto extra_request_headers = + std::get<1>(post_interceptor_->GetRequests()[0]); + EXPECT_TRUE(extra_request_headers.HasHeader("X-Goog-Update-Interactivity")); + std::string header; + extra_request_headers.GetHeader("X-Goog-Update-Interactivity", &header); + EXPECT_STREQ(is_foreground ? "fg" : "bg", header.c_str()); +} + +// Tests that the request succeeds using the second url after the first url +// has failed. +TEST_F(RequestSenderTest, RequestSendSuccessWithFallback) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("test"), net::HTTP_FORBIDDEN)); + EXPECT_TRUE( + post_interceptor_->ExpectRequest(std::make_unique<PartialMatch>("test"))); + + request_sender_->Send( + {GURL(kUrl1), GURL(kUrl2)}, {}, "test", false, + base::BindOnce(&RequestSenderTest::RequestSenderComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(2, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(2, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl1))) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl2))) + << post_interceptor_->GetRequestsAsString(); + + EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str()); + EXPECT_STREQ("test", post_interceptor_->GetRequestBody(1).c_str()); + EXPECT_EQ(0, error_); +} + +// Tests that the request fails when both urls have failed. +TEST_F(RequestSenderTest, RequestSendFailed) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("test"), net::HTTP_FORBIDDEN)); + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("test"), net::HTTP_FORBIDDEN)); + + const std::vector<GURL> urls = {GURL(kUrl1), GURL(kUrl2)}; + request_sender_ = std::make_unique<RequestSender>(config_); + request_sender_->Send( + urls, {}, "test", false, + base::BindOnce(&RequestSenderTest::RequestSenderComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(2, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(2, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl1))) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl2))) + << post_interceptor_->GetRequestsAsString(); + + EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str()); + EXPECT_STREQ("test", post_interceptor_->GetRequestBody(1).c_str()); + EXPECT_EQ(403, error_); +} + +// Tests that the request fails when no urls are provided. +TEST_F(RequestSenderTest, RequestSendFailedNoUrls) { + std::vector<GURL> urls; + request_sender_ = std::make_unique<RequestSender>(config_); + request_sender_->Send( + urls, {}, "test", false, + base::BindOnce(&RequestSenderTest::RequestSenderComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(-10002, error_); +} + +// Tests that a CUP request fails if the response is not signed. +TEST_F(RequestSenderTest, RequestSendCupError) { + EXPECT_TRUE( + post_interceptor_->ExpectRequest(std::make_unique<PartialMatch>("test"), + test_file("updatecheck_reply_1.json"))); + + const std::vector<GURL> urls = {GURL(kUrl1)}; + request_sender_ = std::make_unique<RequestSender>(config_); + request_sender_->Send( + urls, {}, "test", true, + base::BindOnce(&RequestSenderTest::RequestSenderComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(1, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str()); + EXPECT_EQ(-10000, error_); + EXPECT_TRUE(response_.empty()); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/task.h b/src/updater_components/update_client/task.h new file mode 100644 index 0000000..e9b480e --- /dev/null +++ b/src/updater_components/update_client/task.h
@@ -0,0 +1,43 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_TASK_H_ +#define COMPONENTS_UPDATE_CLIENT_TASK_H_ + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace update_client { + +// Defines an abstraction for a unit of work done by the update client. +// Each invocation of the update client API results in a task being created and +// run. In most cases, a task corresponds to a set of CRXs, which are updated +// together. +class Task : public base::RefCounted<Task> { + public: + Task() = default; + virtual void Run() = 0; + + // Does a best effort attempt to make a task release its resources and stop + // soon. It is possible that a running task may complete even if this + // method is called. + virtual void Cancel() = 0; + + // Returns the ids corresponding to the CRXs associated with this update task. + virtual std::vector<std::string> GetIds() const = 0; + + protected: + friend class base::RefCounted<Task>; + virtual ~Task() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(Task); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_TASK_H_
diff --git a/src/updater_components/update_client/task_send_uninstall_ping.cc b/src/updater_components/update_client/task_send_uninstall_ping.cc new file mode 100644 index 0000000..018bb1d --- /dev/null +++ b/src/updater_components/update_client/task_send_uninstall_ping.cc
@@ -0,0 +1,64 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "components/update_client/task_send_uninstall_ping.h" + +#include <utility> + +#include "base/bind.h" +#include "base/location.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/version.h" +#include "components/update_client/update_client.h" +#include "components/update_client/update_engine.h" + +namespace update_client { + +TaskSendUninstallPing::TaskSendUninstallPing( + scoped_refptr<UpdateEngine> update_engine, + const std::string& id, + const base::Version& version, + int reason, + Callback callback) + : update_engine_(update_engine), + id_(id), + version_(version), + reason_(reason), + callback_(std::move(callback)) {} + +TaskSendUninstallPing::~TaskSendUninstallPing() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void TaskSendUninstallPing::Run() { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (id_.empty()) { + TaskComplete(Error::INVALID_ARGUMENT); + return; + } + + update_engine_->SendUninstallPing( + id_, version_, reason_, + base::BindOnce(&TaskSendUninstallPing::TaskComplete, this)); +} + +void TaskSendUninstallPing::Cancel() { + DCHECK(thread_checker_.CalledOnValidThread()); + + TaskComplete(Error::UPDATE_CANCELED); +} + +std::vector<std::string> TaskSendUninstallPing::GetIds() const { + return std::vector<std::string>{id_}; +} + +void TaskSendUninstallPing::TaskComplete(Error error) { + DCHECK(thread_checker_.CalledOnValidThread()); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(callback_), scoped_refptr<Task>(this), error)); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/task_send_uninstall_ping.h b/src/updater_components/update_client/task_send_uninstall_ping.h new file mode 100644 index 0000000..9367b3d --- /dev/null +++ b/src/updater_components/update_client/task_send_uninstall_ping.h
@@ -0,0 +1,68 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_TASK_SEND_UNINSTALL_PING_H_ +#define COMPONENTS_UPDATE_CLIENT_TASK_SEND_UNINSTALL_PING_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "components/update_client/task.h" +#include "components/update_client/update_client.h" + +namespace base { +class Version; +} + +namespace update_client { + +class UpdateEngine; +enum class Error; + +// Defines a specialized task for sending the uninstall ping. +class TaskSendUninstallPing : public Task { + public: + using Callback = + base::OnceCallback<void(scoped_refptr<Task> task, Error error)>; + + // |update_engine| is injected here to handle the task. + // |id| represents the CRX to send the ping for. + // |callback| is called to return the execution flow back to creator of + // this task when the task is done. + TaskSendUninstallPing(scoped_refptr<UpdateEngine> update_engine, + const std::string& id, + const base::Version& version, + int reason, + Callback callback); + + void Run() override; + + void Cancel() override; + + std::vector<std::string> GetIds() const override; + + private: + ~TaskSendUninstallPing() override; + + // Called when the task has completed either because the task has run or + // it has been canceled. + void TaskComplete(Error error); + + base::ThreadChecker thread_checker_; + scoped_refptr<UpdateEngine> update_engine_; + const std::string id_; + const base::Version version_; + int reason_; + Callback callback_; + + DISALLOW_COPY_AND_ASSIGN(TaskSendUninstallPing); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_TASK_SEND_UNINSTALL_PING_H_
diff --git a/src/updater_components/update_client/task_traits.h b/src/updater_components/update_client/task_traits.h new file mode 100644 index 0000000..1f675e7 --- /dev/null +++ b/src/updater_components/update_client/task_traits.h
@@ -0,0 +1,28 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_TASK_TRAITS_H_ +#define COMPONENTS_UPDATE_CLIENT_TASK_TRAITS_H_ + +#include "base/task/task_traits.h" + +namespace update_client { + +constexpr base::TaskTraits kTaskTraits = { + base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT, + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}; + +constexpr base::TaskTraits kTaskTraitsBackgroundDownloader = { + base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT, + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}; + +// This task joins a process, hence .WithBaseSyncPrimitives(). +constexpr base::TaskTraits kTaskTraitsRunCommand = { + base::ThreadPool(), base::MayBlock(), base::WithBaseSyncPrimitives(), + base::TaskPriority::BEST_EFFORT, + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_TASK_TRAITS_H_
diff --git a/src/updater_components/update_client/task_update.cc b/src/updater_components/update_client/task_update.cc new file mode 100644 index 0000000..ab445b5 --- /dev/null +++ b/src/updater_components/update_client/task_update.cc
@@ -0,0 +1,61 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "components/update_client/task_update.h" + +#include <utility> + +#include "base/bind.h" +#include "base/location.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/update_client/update_client.h" +#include "components/update_client/update_engine.h" + +namespace update_client { + +TaskUpdate::TaskUpdate(scoped_refptr<UpdateEngine> update_engine, + bool is_foreground, + const std::vector<std::string>& ids, + UpdateClient::CrxDataCallback crx_data_callback, + Callback callback) + : update_engine_(update_engine), + is_foreground_(is_foreground), + ids_(ids), + crx_data_callback_(std::move(crx_data_callback)), + callback_(std::move(callback)) {} + +TaskUpdate::~TaskUpdate() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void TaskUpdate::Run() { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (ids_.empty()) { + TaskComplete(Error::INVALID_ARGUMENT); + return; + } + + update_engine_->Update(is_foreground_, ids_, std::move(crx_data_callback_), + base::BindOnce(&TaskUpdate::TaskComplete, this)); +} + +void TaskUpdate::Cancel() { + DCHECK(thread_checker_.CalledOnValidThread()); + + TaskComplete(Error::UPDATE_CANCELED); +} + +std::vector<std::string> TaskUpdate::GetIds() const { + return ids_; +} + +void TaskUpdate::TaskComplete(Error error) { + DCHECK(thread_checker_.CalledOnValidThread()); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback_), + scoped_refptr<TaskUpdate>(this), error)); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/task_update.h b/src/updater_components/update_client/task_update.h new file mode 100644 index 0000000..4df796b --- /dev/null +++ b/src/updater_components/update_client/task_update.h
@@ -0,0 +1,66 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_TASK_UPDATE_H_ +#define COMPONENTS_UPDATE_CLIENT_TASK_UPDATE_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "components/update_client/task.h" +#include "components/update_client/update_client.h" + +namespace update_client { + +class UpdateEngine; +enum class Error; + +// Defines a specialized task for updating a group of CRXs. +class TaskUpdate : public Task { + public: + using Callback = + base::OnceCallback<void(scoped_refptr<Task> task, Error error)>; + + // |update_engine| is injected here to handle the task. + // |is_foreground| is true when the update task is initiated by the user. + // |ids| represents the CRXs to be updated by this task. + // |crx_data_callback| is called to get update data for the these CRXs. + // |callback| is called to return the execution flow back to creator of + // this task when the task is done. + TaskUpdate(scoped_refptr<UpdateEngine> update_engine, + bool is_foreground, + const std::vector<std::string>& ids, + UpdateClient::CrxDataCallback crx_data_callback, + Callback callback); + + void Run() override; + + void Cancel() override; + + std::vector<std::string> GetIds() const override; + + private: + ~TaskUpdate() override; + + // Called when the task has completed either because the task has run or + // it has been canceled. + void TaskComplete(Error error); + + base::ThreadChecker thread_checker_; + scoped_refptr<UpdateEngine> update_engine_; + const bool is_foreground_; + const std::vector<std::string> ids_; + UpdateClient::CrxDataCallback crx_data_callback_; + Callback callback_; + + DISALLOW_COPY_AND_ASSIGN(TaskUpdate); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_TASK_UPDATE_H_
diff --git a/src/updater_components/update_client/test_configurator.cc b/src/updater_components/update_client/test_configurator.cc new file mode 100644 index 0000000..9365726 --- /dev/null +++ b/src/updater_components/update_client/test_configurator.cc
@@ -0,0 +1,217 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/test_configurator.h" + +#include <utility> + +#include "base/threading/thread_task_runner_handle.h" +#include "base/version.h" +#include "components/prefs/pref_service.h" +#include "components/services/patch/in_process_file_patcher.h" +#include "components/services/unzip/in_process_unzipper.h" +#include "components/update_client/activity_data_service.h" +#include "components/update_client/net/network_chromium.h" +#include "components/update_client/patch/patch_impl.h" +#include "components/update_client/patcher.h" +#include "components/update_client/protocol_handler.h" +#include "components/update_client/unzip/unzip_impl.h" +#include "components/update_client/unzipper.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "url/gurl.h" + +namespace update_client { + +namespace { + +std::vector<GURL> MakeDefaultUrls() { + std::vector<GURL> urls; + urls.push_back(GURL(POST_INTERCEPT_SCHEME + "://" POST_INTERCEPT_HOSTNAME POST_INTERCEPT_PATH)); + return urls; +} + +} // namespace + +TestConfigurator::TestConfigurator() + : brand_("TEST"), + initial_time_(0), + ondemand_time_(0), + enabled_cup_signing_(false), + enabled_component_updates_(true), + unzip_factory_(base::MakeRefCounted<update_client::UnzipChromiumFactory>( + base::BindRepeating(&unzip::LaunchInProcessUnzipper))), + patch_factory_(base::MakeRefCounted<update_client::PatchChromiumFactory>( + base::BindRepeating(&patch::LaunchInProcessFilePatcher))), + test_shared_loader_factory_( + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( + &test_url_loader_factory_)), + network_fetcher_factory_( + base::MakeRefCounted<NetworkFetcherChromiumFactory>( + test_shared_loader_factory_)) {} + +TestConfigurator::~TestConfigurator() { +} + +int TestConfigurator::InitialDelay() const { + return initial_time_; +} + +int TestConfigurator::NextCheckDelay() const { + return 1; +} + +int TestConfigurator::OnDemandDelay() const { + return ondemand_time_; +} + +int TestConfigurator::UpdateDelay() const { + return 1; +} + +std::vector<GURL> TestConfigurator::UpdateUrl() const { + if (!update_check_url_.is_empty()) + return std::vector<GURL>(1, update_check_url_); + + return MakeDefaultUrls(); +} + +std::vector<GURL> TestConfigurator::PingUrl() const { + if (!ping_url_.is_empty()) + return std::vector<GURL>(1, ping_url_); + + return UpdateUrl(); +} + +std::string TestConfigurator::GetProdId() const { + return "fake_prodid"; +} + +base::Version TestConfigurator::GetBrowserVersion() const { + // Needs to be larger than the required version in tested component manifests. + return base::Version("30.0"); +} + +std::string TestConfigurator::GetChannel() const { + return "fake_channel_string"; +} + +std::string TestConfigurator::GetBrand() const { + return brand_; +} + +std::string TestConfigurator::GetLang() const { + return "fake_lang"; +} + +std::string TestConfigurator::GetOSLongName() const { + return "Fake Operating System"; +} + +base::flat_map<std::string, std::string> TestConfigurator::ExtraRequestParams() + const { + return {{"extra", "foo"}}; +} + +std::string TestConfigurator::GetDownloadPreference() const { + return download_preference_; +} + +scoped_refptr<NetworkFetcherFactory> +TestConfigurator::GetNetworkFetcherFactory() { + return network_fetcher_factory_; +} + +scoped_refptr<UnzipperFactory> TestConfigurator::GetUnzipperFactory() { + return unzip_factory_; +} + +scoped_refptr<PatcherFactory> TestConfigurator::GetPatcherFactory() { + return patch_factory_; +} + +bool TestConfigurator::EnabledDeltas() const { + return true; +} + +bool TestConfigurator::EnabledComponentUpdates() const { + return enabled_component_updates_; +} + +bool TestConfigurator::EnabledBackgroundDownloader() const { + return false; +} + +bool TestConfigurator::EnabledCupSigning() const { + return enabled_cup_signing_; +} + +void TestConfigurator::SetBrand(const std::string& brand) { + brand_ = brand; +} + +void TestConfigurator::SetOnDemandTime(int seconds) { + ondemand_time_ = seconds; +} + +void TestConfigurator::SetInitialDelay(int seconds) { + initial_time_ = seconds; +} + +void TestConfigurator::SetEnabledCupSigning(bool enabled_cup_signing) { + enabled_cup_signing_ = enabled_cup_signing; +} + +void TestConfigurator::SetEnabledComponentUpdates( + bool enabled_component_updates) { + enabled_component_updates_ = enabled_component_updates; +} + +void TestConfigurator::SetDownloadPreference( + const std::string& download_preference) { + download_preference_ = download_preference; +} + +void TestConfigurator::SetUpdateCheckUrl(const GURL& url) { + update_check_url_ = url; +} + +void TestConfigurator::SetPingUrl(const GURL& url) { + ping_url_ = url; +} + +void TestConfigurator::SetAppGuid(const std::string& app_guid) { + app_guid_ = app_guid; +} + +PrefService* TestConfigurator::GetPrefService() const { + return nullptr; +} + +ActivityDataService* TestConfigurator::GetActivityDataService() const { + return nullptr; +} + +bool TestConfigurator::IsPerUserInstall() const { + return true; +} + +std::vector<uint8_t> TestConfigurator::GetRunActionKeyHash() const { + return std::vector<uint8_t>(std::begin(gjpm_hash), std::end(gjpm_hash)); +} + +std::string TestConfigurator::GetAppGuid() const { + return app_guid_; +} + +std::unique_ptr<ProtocolHandlerFactory> +TestConfigurator::GetProtocolHandlerFactory() const { + return std::make_unique<ProtocolHandlerFactoryJSON>(); +} + +RecoveryCRXElevator TestConfigurator::GetRecoveryCRXElevator() const { + return {}; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/test_configurator.h b/src/updater_components/update_client/test_configurator.h new file mode 100644 index 0000000..6935347 --- /dev/null +++ b/src/updater_components/update_client/test_configurator.h
@@ -0,0 +1,146 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_TEST_CONFIGURATOR_H_ +#define COMPONENTS_UPDATE_CLIENT_TEST_CONFIGURATOR_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/containers/flat_map.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/update_client/configurator.h" +#include "services/network/test/test_url_loader_factory.h" +#include "url/gurl.h" + +class PrefService; + +namespace network { +class SharedURLLoaderFactory; +} // namespace network + +namespace update_client { + +class ActivityDataService; +class NetworkFetcherFactory; +class PatchChromiumFactory; +class ProtocolHandlerFactory; +class UnzipChromiumFactory; + +#define POST_INTERCEPT_SCHEME "https" +#define POST_INTERCEPT_HOSTNAME "localhost2" +#define POST_INTERCEPT_PATH "/update2" + +// component 1 has extension id "jebgalgnebhfojomionfpkfelancnnkf", and +// the RSA public key the following hash: +const uint8_t jebg_hash[] = {0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec, + 0x8e, 0xd5, 0xfa, 0x54, 0xb0, 0xd2, 0xdd, 0xa5, + 0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47, 0xf6, 0xc4, + 0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40}; +// component 1 public key (base64 encoded): +const char jebg_public_key[] = + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC68bW8i/RzSaeXOcNLuBw0SP9+1bdo5ysLqH" + "qfLqZs6XyJWEyL0U6f1axPR6LwViku21kgdc6PI524eb8Cr+a/iXGgZ8SdvZTcfQ/g/ukwlblF" + "mtqYfDoVpz03U8rDQ9b6DxeJBF4r48TNlFORggrAiNR26qbf1i178Au12AzWtwIDAQAB"; +// component 2 has extension id "abagagagagagagagagagagagagagagag", and +// the RSA public key the following hash: +const uint8_t abag_hash[] = {0x01, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x01}; +// component 3 has extension id "ihfokbkgjpifnbbojhneepfflplebdkc", and +// the RSA public key the following hash: +const uint8_t ihfo_hash[] = {0x87, 0x5e, 0xa1, 0xa6, 0x9f, 0x85, 0xd1, 0x1e, + 0x97, 0xd4, 0x4f, 0x55, 0xbf, 0xb4, 0x13, 0xa2, + 0xe7, 0xc5, 0xc8, 0xf5, 0x60, 0x19, 0x78, 0x1b, + 0x6d, 0xe9, 0x4c, 0xeb, 0x96, 0x05, 0x42, 0x17}; + +// runaction_test_win.crx and its payload id: gjpmebpgbhcamgdgjcmnjfhggjpgcimm +const uint8_t gjpm_hash[] = {0x69, 0xfc, 0x41, 0xf6, 0x17, 0x20, 0xc6, 0x36, + 0x92, 0xcd, 0x95, 0x76, 0x69, 0xf6, 0x28, 0xcc, + 0xbe, 0x98, 0x4b, 0x93, 0x17, 0xd6, 0x9c, 0xb3, + 0x64, 0x0c, 0x0d, 0x25, 0x61, 0xc5, 0x80, 0x1d}; + +class TestConfigurator : public Configurator { + public: + TestConfigurator(); + + // Overrrides for Configurator. + int InitialDelay() const override; + int NextCheckDelay() const override; + int OnDemandDelay() const override; + int UpdateDelay() const override; + std::vector<GURL> UpdateUrl() const override; + std::vector<GURL> PingUrl() const override; + std::string GetProdId() const override; + base::Version GetBrowserVersion() const override; + std::string GetChannel() const override; + std::string GetBrand() const override; + std::string GetLang() const override; + std::string GetOSLongName() const override; + base::flat_map<std::string, std::string> ExtraRequestParams() const override; + std::string GetDownloadPreference() const override; + scoped_refptr<NetworkFetcherFactory> GetNetworkFetcherFactory() override; + scoped_refptr<UnzipperFactory> GetUnzipperFactory() override; + scoped_refptr<PatcherFactory> GetPatcherFactory() override; + bool EnabledDeltas() const override; + bool EnabledComponentUpdates() const override; + bool EnabledBackgroundDownloader() const override; + bool EnabledCupSigning() const override; + PrefService* GetPrefService() const override; + ActivityDataService* GetActivityDataService() const override; + bool IsPerUserInstall() const override; + std::vector<uint8_t> GetRunActionKeyHash() const override; + std::string GetAppGuid() const override; + std::unique_ptr<ProtocolHandlerFactory> GetProtocolHandlerFactory() + const override; + RecoveryCRXElevator GetRecoveryCRXElevator() const override; + + void SetBrand(const std::string& brand); + void SetOnDemandTime(int seconds); + void SetInitialDelay(int seconds); + void SetDownloadPreference(const std::string& download_preference); + void SetEnabledCupSigning(bool use_cup_signing); + void SetEnabledComponentUpdates(bool enabled_component_updates); + void SetUpdateCheckUrl(const GURL& url); + void SetPingUrl(const GURL& url); + void SetAppGuid(const std::string& app_guid); + network::TestURLLoaderFactory* test_url_loader_factory() { + return &test_url_loader_factory_; + } + + private: + friend class base::RefCountedThreadSafe<TestConfigurator>; + ~TestConfigurator() override; + + class TestPatchService; + + std::string brand_; + int initial_time_; + int ondemand_time_; + std::string download_preference_; + bool enabled_cup_signing_; + bool enabled_component_updates_; + GURL update_check_url_; + GURL ping_url_; + std::string app_guid_; + + scoped_refptr<update_client::UnzipChromiumFactory> unzip_factory_; + scoped_refptr<update_client::PatchChromiumFactory> patch_factory_; + + scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_; + network::TestURLLoaderFactory test_url_loader_factory_; + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory_; + + DISALLOW_COPY_AND_ASSIGN(TestConfigurator); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_TEST_CONFIGURATOR_H_
diff --git a/src/updater_components/update_client/test_installer.cc b/src/updater_components/update_client/test_installer.cc new file mode 100644 index 0000000..0285af0 --- /dev/null +++ b/src/updater_components/update_client/test_installer.cc
@@ -0,0 +1,110 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/test_installer.h" + +#include <string> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#include "base/values.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace update_client { + +TestInstaller::TestInstaller() : error_(0), install_count_(0) { +} + +TestInstaller::~TestInstaller() { + // The unpack path is deleted unconditionally by the component state code, + // which is driving this installer. Therefore, the unpack path must not + // exist when this object is destroyed. + if (!unpack_path_.empty()) + EXPECT_FALSE(base::DirectoryExists(unpack_path_)); +} + +void TestInstaller::OnUpdateError(int error) { + error_ = error; +} + +void TestInstaller::Install(const base::FilePath& unpack_path, + const std::string& /*public_key*/, + Callback callback) { + ++install_count_; + unpack_path_ = unpack_path; + + InstallComplete(std::move(callback), Result(InstallError::NONE)); +} + +void TestInstaller::InstallComplete(Callback callback, + const Result& result) const { + base::PostTask(FROM_HERE, {base::ThreadPool(), base::MayBlock()}, + base::BindOnce(std::move(callback), result)); +} + +bool TestInstaller::GetInstalledFile(const std::string& file, + base::FilePath* installed_file) { + return false; +} + +bool TestInstaller::Uninstall() { + return false; +} + +ReadOnlyTestInstaller::ReadOnlyTestInstaller(const base::FilePath& install_dir) + : install_directory_(install_dir) { +} + +ReadOnlyTestInstaller::~ReadOnlyTestInstaller() { +} + +bool ReadOnlyTestInstaller::GetInstalledFile(const std::string& file, + base::FilePath* installed_file) { + *installed_file = install_directory_.AppendASCII(file); + return true; +} + +VersionedTestInstaller::VersionedTestInstaller() { + base::CreateNewTempDirectory(FILE_PATH_LITERAL("TEST_"), &install_directory_); +} + +VersionedTestInstaller::~VersionedTestInstaller() { + base::DeleteFile(install_directory_, true); +} + +void VersionedTestInstaller::Install(const base::FilePath& unpack_path, + const std::string& public_key, + Callback callback) { + const auto manifest = update_client::ReadManifest(unpack_path); + std::string version_string; + manifest->GetStringASCII("version", &version_string); + const base::Version version(version_string); + + const base::FilePath path = + install_directory_.AppendASCII(version.GetString()); + base::CreateDirectory(path.DirName()); + if (!base::Move(unpack_path, path)) { + InstallComplete(std::move(callback), Result(InstallError::GENERIC_ERROR)); + return; + } + current_version_ = version; + ++install_count_; + + InstallComplete(std::move(callback), Result(InstallError::NONE)); +} + +bool VersionedTestInstaller::GetInstalledFile(const std::string& file, + base::FilePath* installed_file) { + const base::FilePath path = + install_directory_.AppendASCII(current_version_.GetString()); + *installed_file = path.Append(base::FilePath::FromUTF8Unsafe(file)); + return true; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/test_installer.h b/src/updater_components/update_client/test_installer.h new file mode 100644 index 0000000..a12baf9 --- /dev/null +++ b/src/updater_components/update_client/test_installer.h
@@ -0,0 +1,89 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_ +#define COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "base/files/file_path.h" +#include "components/update_client/update_client.h" + +namespace update_client { + +// TODO(sorin): consider reducing the number of the installer mocks. +// A TestInstaller is an installer that does nothing for installation except +// increment a counter. +class TestInstaller : public CrxInstaller { + public: + TestInstaller(); + + void OnUpdateError(int error) override; + + void Install(const base::FilePath& unpack_path, + const std::string& public_key, + Callback callback) override; + + bool GetInstalledFile(const std::string& file, + base::FilePath* installed_file) override; + + bool Uninstall() override; + + int error() const { return error_; } + + int install_count() const { return install_count_; } + + protected: + ~TestInstaller() override; + + void InstallComplete(Callback callback, const Result& result) const; + + int error_; + int install_count_; + + private: + // Contains the |unpack_path| argument of the Install call. + base::FilePath unpack_path_; +}; + +// A ReadOnlyTestInstaller is an installer that knows about files in an existing +// directory. It will not write to the directory. +class ReadOnlyTestInstaller : public TestInstaller { + public: + explicit ReadOnlyTestInstaller(const base::FilePath& installed_path); + + bool GetInstalledFile(const std::string& file, + base::FilePath* installed_file) override; + + private: + ~ReadOnlyTestInstaller() override; + + base::FilePath install_directory_; +}; + +// A VersionedTestInstaller is an installer that installs files into versioned +// directories (e.g. somedir/25.23.89.141/<files>). +class VersionedTestInstaller : public TestInstaller { + public: + VersionedTestInstaller(); + + void Install(const base::FilePath& unpack_path, + const std::string& public_key, + Callback callback) override; + + bool GetInstalledFile(const std::string& file, + base::FilePath* installed_file) override; + + private: + ~VersionedTestInstaller() override; + + base::FilePath install_directory_; + base::Version current_version_; +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_
diff --git a/src/updater_components/update_client/unzip/unzip_impl.cc b/src/updater_components/update_client/unzip/unzip_impl.cc new file mode 100644 index 0000000..1fa9058 --- /dev/null +++ b/src/updater_components/update_client/unzip/unzip_impl.cc
@@ -0,0 +1,39 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/unzip/unzip_impl.h" + +#include "components/services/unzip/public/cpp/unzip.h" + +namespace update_client { + +namespace { + +class UnzipperImpl : public Unzipper { + public: + explicit UnzipperImpl(UnzipChromiumFactory::Callback callback) + : callback_(std::move(callback)) {} + + void Unzip(const base::FilePath& zip_file, + const base::FilePath& destination, + UnzipCompleteCallback callback) override { + unzip::Unzip(callback_.Run(), zip_file, destination, std::move(callback)); + } + + private: + const UnzipChromiumFactory::Callback callback_; +}; + +} // namespace + +UnzipChromiumFactory::UnzipChromiumFactory(Callback callback) + : callback_(std::move(callback)) {} + +std::unique_ptr<Unzipper> UnzipChromiumFactory::Create() const { + return std::make_unique<UnzipperImpl>(callback_); +} + +UnzipChromiumFactory::~UnzipChromiumFactory() = default; + +} // namespace update_client
diff --git a/src/updater_components/update_client/unzip/unzip_impl.h b/src/updater_components/update_client/unzip/unzip_impl.h new file mode 100644 index 0000000..bbf9156 --- /dev/null +++ b/src/updater_components/update_client/unzip/unzip_impl.h
@@ -0,0 +1,38 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UNZIP_UNZIP_IMPL_H_ +#define COMPONENTS_UPDATE_CLIENT_UNZIP_UNZIP_IMPL_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/services/unzip/public/mojom/unzipper.mojom.h" +#include "components/update_client/unzipper.h" +#include "mojo/public/cpp/bindings/pending_remote.h" + +namespace update_client { + +class UnzipChromiumFactory : public UnzipperFactory { + public: + using Callback = + base::RepeatingCallback<mojo::PendingRemote<unzip::mojom::Unzipper>()>; + explicit UnzipChromiumFactory(Callback callback); + + std::unique_ptr<Unzipper> Create() const override; + + protected: + ~UnzipChromiumFactory() override; + + private: + const Callback callback_; + + DISALLOW_COPY_AND_ASSIGN(UnzipChromiumFactory); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UNZIP_UNZIP_IMPL_H_
diff --git a/src/updater_components/update_client/unzipper.h b/src/updater_components/update_client/unzipper.h new file mode 100644 index 0000000..6aed58c --- /dev/null +++ b/src/updater_components/update_client/unzipper.h
@@ -0,0 +1,50 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UNZIPPER_H_ +#define COMPONENTS_UPDATE_CLIENT_UNZIPPER_H_ + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace base { +class FilePath; +} // namespace base + +namespace update_client { + +class Unzipper { + public: + using UnzipCompleteCallback = base::OnceCallback<void(bool success)>; + + virtual ~Unzipper() = default; + + virtual void Unzip(const base::FilePath& zip_file, + const base::FilePath& destination, + UnzipCompleteCallback callback) = 0; + + protected: + Unzipper() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(Unzipper); +}; + +class UnzipperFactory : public base::RefCountedThreadSafe<UnzipperFactory> { + public: + virtual std::unique_ptr<Unzipper> Create() const = 0; + + protected: + friend class base::RefCountedThreadSafe<UnzipperFactory>; + UnzipperFactory() = default; + virtual ~UnzipperFactory() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(UnzipperFactory); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UNZIPPER_H_
diff --git a/src/updater_components/update_client/update_checker.cc b/src/updater_components/update_client/update_checker.cc new file mode 100644 index 0000000..cfc2578 --- /dev/null +++ b/src/updater_components/update_client/update_checker.cc
@@ -0,0 +1,301 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/update_checker.h" + +#include <stddef.h> + +#include <algorithm> +#include <functional> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/strings/stringprintf.h" +#include "base/task/post_task.h" +#include "base/threading/thread_checker.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "components/update_client/component.h" +#include "components/update_client/configurator.h" +#include "components/update_client/persisted_data.h" +#include "components/update_client/protocol_definition.h" +#include "components/update_client/protocol_handler.h" +#include "components/update_client/protocol_serializer.h" +#include "components/update_client/request_sender.h" +#include "components/update_client/task_traits.h" +#include "components/update_client/update_client.h" +#include "components/update_client/updater_state.h" +#include "components/update_client/utils.h" +#include "url/gurl.h" + +namespace update_client { + +namespace { + +// Returns a sanitized version of the brand or an empty string otherwise. +std::string SanitizeBrand(const std::string& brand) { + return IsValidBrand(brand) ? brand : std::string(""); +} + +// Returns true if at least one item requires network encryption. +bool IsEncryptionRequired(const IdToComponentPtrMap& components) { + for (const auto& item : components) { + const auto& component = item.second; + if (component->crx_component() && + component->crx_component()->requires_network_encryption) + return true; + } + return false; +} + +// Filters invalid attributes from |installer_attributes|. +using InstallerAttributesFlatMap = base::flat_map<std::string, std::string>; +InstallerAttributesFlatMap SanitizeInstallerAttributes( + const InstallerAttributes& installer_attributes) { + InstallerAttributesFlatMap sanitized_attrs; + for (const auto& attr : installer_attributes) { + if (IsValidInstallerAttribute(attr)) + sanitized_attrs.insert(attr); + } + return sanitized_attrs; +} + +class UpdateCheckerImpl : public UpdateChecker { + public: + UpdateCheckerImpl(scoped_refptr<Configurator> config, + PersistedData* metadata); + ~UpdateCheckerImpl() override; + + // Overrides for UpdateChecker. + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_checked, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override; + + private: + void ReadUpdaterStateAttributes(); + void CheckForUpdatesHelper( + const std::string& session_id, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates); + void OnRequestSenderComplete(int error, + const std::string& response, + int retry_after_sec); + void UpdateCheckSucceeded(const ProtocolParser::Results& results, + int retry_after_sec); + void UpdateCheckFailed(ErrorCategory error_category, + int error, + int retry_after_sec); + + base::ThreadChecker thread_checker_; + + const scoped_refptr<Configurator> config_; + PersistedData* metadata_ = nullptr; + std::vector<std::string> ids_checked_; + UpdateCheckCallback update_check_callback_; + std::unique_ptr<UpdaterState::Attributes> updater_state_attributes_; + std::unique_ptr<RequestSender> request_sender_; + + DISALLOW_COPY_AND_ASSIGN(UpdateCheckerImpl); +}; + +UpdateCheckerImpl::UpdateCheckerImpl(scoped_refptr<Configurator> config, + PersistedData* metadata) + : config_(config), metadata_(metadata) {} + +UpdateCheckerImpl::~UpdateCheckerImpl() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void UpdateCheckerImpl::CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_checked, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + ids_checked_ = ids_checked; + update_check_callback_ = std::move(update_check_callback); + + base::PostTaskAndReply( + FROM_HERE, kTaskTraits, + base::BindOnce(&UpdateCheckerImpl::ReadUpdaterStateAttributes, + base::Unretained(this)), + base::BindOnce(&UpdateCheckerImpl::CheckForUpdatesHelper, + base::Unretained(this), session_id, std::cref(components), + additional_attributes, enabled_component_updates)); +} + +// This function runs on the blocking pool task runner. +void UpdateCheckerImpl::ReadUpdaterStateAttributes() { +#if defined(OS_WIN) + // On Windows, the Chrome and the updater install modes are matched by design. + updater_state_attributes_ = + UpdaterState::GetState(!config_->IsPerUserInstall()); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + // MacOS ignores this value in the current implementation but this may change. + updater_state_attributes_ = UpdaterState::GetState(false); +#else +// Other platforms don't have updaters. +#endif // OS_WIN +} + +void UpdateCheckerImpl::CheckForUpdatesHelper( + const std::string& session_id, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates) { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto urls(config_->UpdateUrl()); + if (IsEncryptionRequired(components)) + RemoveUnsecureUrls(&urls); + + // Components in this update check are either all foreground, or all + // background since this member is inherited from the component's update + // context. Pick the state of the first component to use in the update check. + DCHECK(!components.empty()); + const bool is_foreground = components.at(ids_checked_[0])->is_foreground(); + DCHECK( + std::all_of(components.cbegin(), components.cend(), + [is_foreground](IdToComponentPtrMap::const_reference& elem) { + return is_foreground == elem.second->is_foreground(); + })); + + std::vector<protocol_request::App> apps; + for (const auto& app_id : ids_checked_) { + DCHECK_EQ(1u, components.count(app_id)); + const auto& component = components.at(app_id); + DCHECK_EQ(component->id(), app_id); + const auto& crx_component = component->crx_component(); + DCHECK(crx_component); + + std::string install_source; + if (!crx_component->install_source.empty()) + install_source = crx_component->install_source; + else if (component->is_foreground()) + install_source = "ondemand"; + + const bool is_update_disabled = + crx_component->supports_group_policy_enable_component_updates && + !enabled_component_updates; + + apps.push_back(MakeProtocolApp( + app_id, crx_component->version, SanitizeBrand(config_->GetBrand()), + install_source, crx_component->install_location, + crx_component->fingerprint, + SanitizeInstallerAttributes(crx_component->installer_attributes), + metadata_->GetCohort(app_id), metadata_->GetCohortName(app_id), + metadata_->GetCohortHint(app_id), crx_component->disabled_reasons, + MakeProtocolUpdateCheck(is_update_disabled), + MakeProtocolPing(app_id, metadata_))); + } + + const auto request = MakeProtocolRequest( + session_id, config_->GetProdId(), + config_->GetBrowserVersion().GetString(), config_->GetLang(), + config_->GetChannel(), config_->GetOSLongName(), + config_->GetDownloadPreference(), additional_attributes, + updater_state_attributes_.get(), std::move(apps)); + + request_sender_ = std::make_unique<RequestSender>(config_); + request_sender_->Send( + urls, + BuildUpdateCheckExtraRequestHeaders(config_->GetProdId(), + config_->GetBrowserVersion(), + ids_checked_, is_foreground), + config_->GetProtocolHandlerFactory()->CreateSerializer()->Serialize( + request), + config_->EnabledCupSigning(), + base::BindOnce(&UpdateCheckerImpl::OnRequestSenderComplete, + base::Unretained(this))); +} + +void UpdateCheckerImpl::OnRequestSenderComplete( + int error, + const std::string& response, + int retry_after_sec) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (error) { + VLOG(1) << "RequestSender failed " << error; + UpdateCheckFailed(ErrorCategory::kUpdateCheck, error, retry_after_sec); + return; + } + + auto parser = config_->GetProtocolHandlerFactory()->CreateParser(); + if (!parser->Parse(response)) { + VLOG(1) << "Parse failed " << parser->errors(); + UpdateCheckFailed(ErrorCategory::kUpdateCheck, + static_cast<int>(ProtocolError::PARSE_FAILED), + retry_after_sec); + return; + } + + DCHECK_EQ(0, error); + UpdateCheckSucceeded(parser->results(), retry_after_sec); +} + +void UpdateCheckerImpl::UpdateCheckSucceeded( + const ProtocolParser::Results& results, + int retry_after_sec) { + DCHECK(thread_checker_.CalledOnValidThread()); + + const int daynum = results.daystart_elapsed_days; + if (daynum != ProtocolParser::kNoDaystart) { + metadata_->SetDateLastActive(ids_checked_, daynum); + metadata_->SetDateLastRollCall(ids_checked_, daynum); + } + for (const auto& result : results.list) { + auto entry = result.cohort_attrs.find(ProtocolParser::Result::kCohort); + if (entry != result.cohort_attrs.end()) + metadata_->SetCohort(result.extension_id, entry->second); + entry = result.cohort_attrs.find(ProtocolParser::Result::kCohortName); + if (entry != result.cohort_attrs.end()) + metadata_->SetCohortName(result.extension_id, entry->second); + entry = result.cohort_attrs.find(ProtocolParser::Result::kCohortHint); + if (entry != result.cohort_attrs.end()) + metadata_->SetCohortHint(result.extension_id, entry->second); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(update_check_callback_), + base::make_optional<ProtocolParser::Results>(results), + ErrorCategory::kNone, 0, retry_after_sec)); +} + +void UpdateCheckerImpl::UpdateCheckFailed(ErrorCategory error_category, + int error, + int retry_after_sec) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_NE(0, error); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(update_check_callback_), base::nullopt, + error_category, error, retry_after_sec)); +} + +} // namespace + +std::unique_ptr<UpdateChecker> UpdateChecker::Create( + scoped_refptr<Configurator> config, + PersistedData* persistent) { + return std::make_unique<UpdateCheckerImpl>(config, persistent); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/update_checker.h b/src/updater_components/update_client/update_checker.h new file mode 100644 index 0000000..d5126e1 --- /dev/null +++ b/src/updater_components/update_client/update_checker.h
@@ -0,0 +1,67 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_CHECKER_H_ +#define COMPONENTS_UPDATE_CLIENT_UPDATE_CHECKER_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/optional.h" +#include "components/update_client/component.h" +#include "components/update_client/protocol_parser.h" +#include "url/gurl.h" + +namespace update_client { + +class Configurator; +class PersistedData; + +class UpdateChecker { + public: + using UpdateCheckCallback = base::OnceCallback<void( + const base::Optional<ProtocolParser::Results>& results, + ErrorCategory error_category, + int error, + int retry_after_sec)>; + + using Factory = + std::unique_ptr<UpdateChecker> (*)(scoped_refptr<Configurator> config, + PersistedData* persistent); + + virtual ~UpdateChecker() = default; + + // Initiates an update check for the components specified by their ids. + // |additional_attributes| provides a way to customize the <request> element. + // |is_foreground| controls the value of "X-Goog-Update-Interactivity" + // header which is sent with the update check. + // On completion, the state of |components| is mutated as required by the + // server response received. + virtual void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) = 0; + + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* persistent); + + protected: + UpdateChecker() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(UpdateChecker); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_CHECKER_H_
diff --git a/src/updater_components/update_client/update_checker_unittest.cc b/src/updater_components/update_client/update_checker_unittest.cc new file mode 100644 index 0000000..24fc086 --- /dev/null +++ b/src/updater_components/update_client/update_checker_unittest.cc
@@ -0,0 +1,1221 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/update_checker.h" + +#include <map> +#include <memory> +#include <tuple> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/optional.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/stl_util.h" +#include "base/strings/stringprintf.h" +#include "base/task/post_task.h" +#include "base/test/bind_test_util.h" +#include "base/test/task_environment.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/version.h" +#include "build/branding_buildflags.h" +#include "build/build_config.h" +#include "components/prefs/testing_pref_service.h" +#include "components/update_client/activity_data_service.h" +#include "components/update_client/component.h" +#include "components/update_client/net/url_loader_post_interceptor.h" +#include "components/update_client/persisted_data.h" +#include "components/update_client/test_configurator.h" +#include "components/update_client/update_engine.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +using std::string; + +namespace update_client { + +namespace { + +base::FilePath test_file(const char* file) { + base::FilePath path; + base::PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("components") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("update_client") + .AppendASCII(file); +} + +const char kUpdateItemId[] = "jebgalgnebhfojomionfpkfelancnnkf"; + +class ActivityDataServiceTest final : public ActivityDataService { + public: + bool GetActiveBit(const std::string& id) const override; + void ClearActiveBit(const std::string& id) override; + int GetDaysSinceLastActive(const std::string& id) const override; + int GetDaysSinceLastRollCall(const std::string& id) const override; + + void SetActiveBit(const std::string& id, bool value); + void SetDaysSinceLastActive(const std::string& id, int daynum); + void SetDaysSinceLastRollCall(const std::string& id, int daynum); + + private: + std::map<std::string, bool> actives_; + std::map<std::string, int> days_since_last_actives_; + std::map<std::string, int> days_since_last_rollcalls_; +}; + +bool ActivityDataServiceTest::GetActiveBit(const std::string& id) const { + const auto& it = actives_.find(id); + return it != actives_.end() ? it->second : false; +} + +void ActivityDataServiceTest::ClearActiveBit(const std::string& id) { + SetActiveBit(id, false); +} + +int ActivityDataServiceTest::GetDaysSinceLastActive( + const std::string& id) const { + const auto& it = days_since_last_actives_.find(id); + return it != days_since_last_actives_.end() ? it->second : -2; +} + +int ActivityDataServiceTest::GetDaysSinceLastRollCall( + const std::string& id) const { + const auto& it = days_since_last_rollcalls_.find(id); + return it != days_since_last_rollcalls_.end() ? it->second : -2; +} + +void ActivityDataServiceTest::SetActiveBit(const std::string& id, bool value) { + actives_[id] = value; +} + +void ActivityDataServiceTest::SetDaysSinceLastActive(const std::string& id, + int daynum) { + days_since_last_actives_[id] = daynum; +} + +void ActivityDataServiceTest::SetDaysSinceLastRollCall(const std::string& id, + int daynum) { + days_since_last_rollcalls_[id] = daynum; +} + +} // namespace + +class UpdateCheckerTest : public testing::TestWithParam<bool> { + public: + UpdateCheckerTest(); + ~UpdateCheckerTest() override; + + // Overrides from testing::Test. + void SetUp() override; + void TearDown() override; + + void UpdateCheckComplete( + const base::Optional<ProtocolParser::Results>& results, + ErrorCategory error_category, + int error, + int retry_after_sec); + + protected: + void Quit(); + void RunThreads(); + + std::unique_ptr<Component> MakeComponent() const; + + scoped_refptr<TestConfigurator> config_; + std::unique_ptr<ActivityDataServiceTest> activity_data_service_; + std::unique_ptr<TestingPrefServiceSimple> pref_; + std::unique_ptr<PersistedData> metadata_; + + std::unique_ptr<UpdateChecker> update_checker_; + + std::unique_ptr<URLLoaderPostInterceptor> post_interceptor_; + + base::Optional<ProtocolParser::Results> results_; + ErrorCategory error_category_ = ErrorCategory::kNone; + int error_ = 0; + int retry_after_sec_ = 0; + + scoped_refptr<UpdateContext> update_context_; + + bool is_foreground_ = false; + + private: + scoped_refptr<UpdateContext> MakeMockUpdateContext() const; + + base::test::TaskEnvironment task_environment_; + base::OnceClosure quit_closure_; + + DISALLOW_COPY_AND_ASSIGN(UpdateCheckerTest); +}; + +// This test is parameterized for |is_foreground|. +INSTANTIATE_TEST_SUITE_P(Parameterized, UpdateCheckerTest, testing::Bool()); + +UpdateCheckerTest::UpdateCheckerTest() + : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {} + +UpdateCheckerTest::~UpdateCheckerTest() { +} + +void UpdateCheckerTest::SetUp() { + is_foreground_ = GetParam(); + + config_ = base::MakeRefCounted<TestConfigurator>(); + + pref_ = std::make_unique<TestingPrefServiceSimple>(); + activity_data_service_ = std::make_unique<ActivityDataServiceTest>(); + PersistedData::RegisterPrefs(pref_->registry()); + metadata_ = std::make_unique<PersistedData>(pref_.get(), + activity_data_service_.get()); + + post_interceptor_ = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor_); + + update_checker_ = nullptr; + + error_ = 0; + retry_after_sec_ = 0; + update_context_ = MakeMockUpdateContext(); + update_context_->is_foreground = is_foreground_; +} + +void UpdateCheckerTest::TearDown() { + update_checker_ = nullptr; + + post_interceptor_.reset(); + + config_ = nullptr; + + // The PostInterceptor requires the message loop to run to destruct correctly. + // TODO(sorin): This is fragile and should be fixed. + task_environment_.RunUntilIdle(); +} + +void UpdateCheckerTest::RunThreads() { + base::RunLoop runloop; + quit_closure_ = runloop.QuitClosure(); + runloop.Run(); +} + +void UpdateCheckerTest::Quit() { + if (!quit_closure_.is_null()) + std::move(quit_closure_).Run(); +} + +void UpdateCheckerTest::UpdateCheckComplete( + const base::Optional<ProtocolParser::Results>& results, + ErrorCategory error_category, + int error, + int retry_after_sec) { + results_ = results; + error_category_ = error_category; + error_ = error; + retry_after_sec_ = retry_after_sec; + Quit(); +} + +scoped_refptr<UpdateContext> UpdateCheckerTest::MakeMockUpdateContext() const { + return base::MakeRefCounted<UpdateContext>( + config_, false, std::vector<std::string>(), + UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(), + UpdateEngine::Callback(), nullptr); +} + +std::unique_ptr<Component> UpdateCheckerTest::MakeComponent() const { + CrxComponent crx_component; + crx_component.name = "test_jebg"; + crx_component.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx_component.installer = nullptr; + crx_component.version = base::Version("0.9"); + crx_component.fingerprint = "fp1"; + + auto component = std::make_unique<Component>(*update_context_, kUpdateItemId); + component->state_ = std::make_unique<Component::StateNew>(component.get()); + component->crx_component_ = crx_component; + + return component; +} + +// This test is parameterized for |is_foreground|. +TEST_P(UpdateCheckerTest, UpdateCheckSuccess) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + auto& component = components[kUpdateItemId]; + component->crx_component_->installer_attributes["ap"] = "some_ap"; + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, + {{"extra", "params"}, {"testrequest", "1"}}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(1, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + ASSERT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + // Sanity check the request. + const auto root = + base::JSONReader::Read(post_interceptor_->GetRequestBody(0)); + ASSERT_TRUE(root); + const auto* request = root->FindKey("request"); + ASSERT_TRUE(request); + EXPECT_TRUE(request->FindKey("@os")); + EXPECT_EQ("fake_prodid", request->FindKey("@updater")->GetString()); + EXPECT_EQ("crx2,crx3", request->FindKey("acceptformat")->GetString()); + EXPECT_TRUE(request->FindKey("arch")); + EXPECT_EQ("cr", request->FindKey("dedup")->GetString()); + EXPECT_EQ("params", request->FindKey("extra")->GetString()); + EXPECT_LT(0, request->FindPath({"hw", "physmemory"})->GetInt()); + EXPECT_EQ("fake_lang", request->FindKey("lang")->GetString()); + EXPECT_TRUE(request->FindKey("nacl_arch")); + EXPECT_EQ("fake_channel_string", + request->FindKey("prodchannel")->GetString()); + EXPECT_EQ("30.0", request->FindKey("prodversion")->GetString()); + EXPECT_EQ("3.1", request->FindKey("protocol")->GetString()); + EXPECT_TRUE(request->FindKey("requestid")); + EXPECT_TRUE(request->FindKey("sessionid")); + EXPECT_EQ("1", request->FindKey("testrequest")->GetString()); + EXPECT_EQ("fake_channel_string", + request->FindKey("updaterchannel")->GetString()); + EXPECT_EQ("30.0", request->FindKey("updaterversion")->GetString()); + + // No "dlpref" is sent by default. + EXPECT_FALSE(request->FindKey("dlpref")); + + EXPECT_TRUE(request->FindPath({"os", "arch"})->is_string()); + EXPECT_EQ("Fake Operating System", + request->FindPath({"os", "platform"})->GetString()); + EXPECT_TRUE(request->FindPath({"os", "version"})->is_string()); + + const auto& app = request->FindKey("app")->GetList()[0]; + EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString()); + EXPECT_EQ("0.9", app.FindKey("version")->GetString()); + EXPECT_EQ("TEST", app.FindKey("brand")->GetString()); + if (is_foreground_) + EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString()); + EXPECT_EQ("some_ap", app.FindKey("ap")->GetString()); + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_TRUE(app.FindKey("updatecheck")); + EXPECT_TRUE(app.FindKey("ping")); + EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt()); + EXPECT_EQ("fp1", app.FindPath({"packages", "package"}) + ->GetList()[0] + .FindKey("fp") + ->GetString()); + +#if defined(OS_WIN) + EXPECT_TRUE(request->FindKey("domainjoined")); +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + const auto* updater = request->FindKey("updater"); + EXPECT_TRUE(updater); + EXPECT_EQ("Omaha", updater->FindKey("name")->GetString()); + EXPECT_TRUE(updater->FindKey("autoupdatecheckenabled")->is_bool()); + EXPECT_TRUE(updater->FindKey("ismachine")->is_bool()); + EXPECT_TRUE(updater->FindKey("updatepolicy")->is_int()); +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) +#endif // OS_WIN + + // Sanity check the arguments of the callback after parsing. + EXPECT_EQ(ErrorCategory::kNone, error_category_); + EXPECT_EQ(0, error_); + EXPECT_TRUE(results_); + EXPECT_EQ(1u, results_->list.size()); + const auto& result = results_->list.front(); + EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", result.extension_id.c_str()); + EXPECT_EQ("1.0", result.manifest.version); + EXPECT_EQ("11.0.1.0", result.manifest.browser_min_version); + EXPECT_EQ(1u, result.manifest.packages.size()); + EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf.crx", + result.manifest.packages.front().name.c_str()); + EXPECT_EQ(1u, result.crx_urls.size()); + EXPECT_EQ(GURL("http://localhost/download/"), result.crx_urls.front()); + EXPECT_STREQ("this", result.action_run.c_str()); + + // Check the DDOS protection header values. + const auto extra_request_headers = + std::get<1>(post_interceptor_->GetRequests()[0]); + EXPECT_TRUE(extra_request_headers.HasHeader("X-Goog-Update-Interactivity")); + std::string header; + extra_request_headers.GetHeader("X-Goog-Update-Interactivity", &header); + EXPECT_STREQ(is_foreground_ ? "fg" : "bg", header.c_str()); + extra_request_headers.GetHeader("X-Goog-Update-Updater", &header); + EXPECT_STREQ("fake_prodid-30.0", header.c_str()); + extra_request_headers.GetHeader("X-Goog-Update-AppId", &header); + EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", header.c_str()); +} + +// Tests that an invalid "ap" is not serialized. +TEST_P(UpdateCheckerTest, UpdateCheckInvalidAp) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + // Make "ap" too long. + auto& component = components[kUpdateItemId]; + component->crx_component_->installer_attributes["ap"] = std::string(257, 'a'); + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + + RunThreads(); + + const auto request = post_interceptor_->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString()); + EXPECT_EQ("0.9", app.FindKey("version")->GetString()); + EXPECT_EQ("TEST", app.FindKey("brand")->GetString()); + if (is_foreground_) + EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString()); + EXPECT_FALSE(app.FindKey("ap")); + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_TRUE(app.FindKey("updatecheck")); + EXPECT_TRUE(app.FindKey("ping")); + EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt()); + EXPECT_EQ("fp1", app.FindPath({"packages", "package"}) + ->GetList()[0] + .FindKey("fp") + ->GetString()); +} + +TEST_P(UpdateCheckerTest, UpdateCheckSuccessNoBrand) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + + config_->SetBrand("TOOLONG"); // Sets an invalid brand code. + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + + RunThreads(); + + const auto request = post_interceptor_->GetRequestBody(0); + + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString()); + EXPECT_EQ("0.9", app.FindKey("version")->GetString()); + EXPECT_FALSE(app.FindKey("brand")); + if (is_foreground_) + EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString()); + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_TRUE(app.FindKey("updatecheck")); + EXPECT_TRUE(app.FindKey("ping")); + EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt()); + EXPECT_EQ("fp1", app.FindPath({"packages", "package"}) + ->GetList()[0] + .FindKey("fp") + ->GetString()); +} + +// Simulates a 403 server response error. +TEST_P(UpdateCheckerTest, UpdateCheckError) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), net::HTTP_FORBIDDEN)); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(1, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + EXPECT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_); + EXPECT_EQ(403, error_); + EXPECT_FALSE(results_); +} + +TEST_P(UpdateCheckerTest, UpdateCheckDownloadPreference) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + + config_->SetDownloadPreference(string("cacheable")); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, + {{"extra", "params"}}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + // The request must contain dlpref="cacheable". + const auto request = post_interceptor_->GetRequestBody(0); + const auto root = base::JSONReader().Read(request); + ASSERT_TRUE(root); + EXPECT_EQ("cacheable", + root->FindKey("request")->FindKey("dlpref")->GetString()); +} + +// This test is checking that an update check signed with CUP fails, since there +// is currently no entity that can respond with a valid signed response. +// A proper CUP test requires network mocks, which are not available now. +TEST_P(UpdateCheckerTest, UpdateCheckCupError) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + + config_->SetEnabledCupSigning(true); + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + + RunThreads(); + + EXPECT_EQ(1, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + ASSERT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + // Sanity check the request. + const auto& request = post_interceptor_->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString()); + EXPECT_EQ("0.9", app.FindKey("version")->GetString()); + EXPECT_EQ("TEST", app.FindKey("brand")->GetString()); + if (is_foreground_) + EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString()); + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_TRUE(app.FindKey("updatecheck")); + EXPECT_TRUE(app.FindKey("ping")); + EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt()); + EXPECT_EQ("fp1", app.FindPath({"packages", "package"}) + ->GetList()[0] + .FindKey("fp") + ->GetString()); + + // Expect an error since the response is not trusted. + EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_); + EXPECT_EQ(-10000, error_); + EXPECT_FALSE(results_); +} + +// Tests that the UpdateCheckers will not make an update check for a +// component that requires encryption when the update check URL is unsecure. +TEST_P(UpdateCheckerTest, UpdateCheckRequiresEncryptionError) { + config_->SetUpdateCheckUrl(GURL("http:\\foo\bar")); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + auto& component = components[kUpdateItemId]; + component->crx_component_->requires_network_encryption = true; + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_); + EXPECT_EQ(-10002, error_); + EXPECT_FALSE(component->next_version_.IsValid()); +} + +// Tests that the PersistedData will get correctly update and reserialize +// the elapsed_days value. +TEST_P(UpdateCheckerTest, UpdateCheckLastRollCall) { + const char* filename = "updatecheck_reply_4.json"; + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), test_file(filename))); + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), test_file(filename))); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + // Do two update-checks. + activity_data_service_->SetDaysSinceLastRollCall(kUpdateItemId, 5); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, + {{"extra", "params"}}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, + {{"extra", "params"}}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(2, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + ASSERT_EQ(2, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + const auto root1 = + base::JSONReader::Read(post_interceptor_->GetRequestBody(0)); + ASSERT_TRUE(root1); + const auto& app1 = root1->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(5, app1.FindPath({"ping", "r"})->GetInt()); + const auto root2 = + base::JSONReader::Read(post_interceptor_->GetRequestBody(1)); + ASSERT_TRUE(root2); + const auto& app2 = root2->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(3383, app2.FindPath({"ping", "rd"})->GetInt()); + EXPECT_TRUE(app2.FindPath({"ping", "ping_freshness"})->is_string()); +} + +TEST_P(UpdateCheckerTest, UpdateCheckLastActive) { + const char* filename = "updatecheck_reply_4.json"; + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), test_file(filename))); + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), test_file(filename))); + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), test_file(filename))); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + activity_data_service_->SetActiveBit(kUpdateItemId, true); + activity_data_service_->SetDaysSinceLastActive(kUpdateItemId, 10); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, + {{"extra", "params"}}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + // The active bit should be reset. + EXPECT_FALSE(metadata_->GetActiveBit(kUpdateItemId)); + + activity_data_service_->SetActiveBit(kUpdateItemId, true); + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, + {{"extra", "params"}}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + // The active bit should be reset. + EXPECT_FALSE(metadata_->GetActiveBit(kUpdateItemId)); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, + {{"extra", "params"}}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_FALSE(metadata_->GetActiveBit(kUpdateItemId)); + + EXPECT_EQ(3, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + ASSERT_EQ(3, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + { + const auto root = + base::JSONReader::Read(post_interceptor_->GetRequestBody(0)); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(10, app.FindPath({"ping", "a"})->GetInt()); + EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt()); + } + { + const auto root = + base::JSONReader::Read(post_interceptor_->GetRequestBody(1)); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(3383, app.FindPath({"ping", "ad"})->GetInt()); + EXPECT_EQ(3383, app.FindPath({"ping", "rd"})->GetInt()); + EXPECT_TRUE(app.FindPath({"ping", "ping_freshness"})->is_string()); + } + { + const auto root = + base::JSONReader::Read(post_interceptor_->GetRequestBody(2)); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(3383, app.FindPath({"ping", "rd"})->GetInt()); + EXPECT_TRUE(app.FindPath({"ping", "ping_freshness"})->is_string()); + } +} + +TEST_P(UpdateCheckerTest, UpdateCheckInstallSource) { + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + auto& component = components[kUpdateItemId]; + auto crx_component = component->crx_component(); + + if (is_foreground_) { + { + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = + root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString()); + EXPECT_FALSE(app.FindKey("installedby")); + } + { + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + crx_component->install_source = "sideload"; + crx_component->install_location = "policy"; + component->set_crx_component(*crx_component); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = + root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ("sideload", app.FindKey("installsource")->GetString()); + EXPECT_EQ("policy", app.FindKey("installedby")->GetString()); + } + return; + } + + DCHECK(!is_foreground_); + { + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_FALSE(app.FindKey("installsource")); + } + { + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + crx_component->install_source = "webstore"; + crx_component->install_location = "external"; + component->set_crx_component(*crx_component); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ("webstore", app.FindKey("installsource")->GetString()); + EXPECT_EQ("external", app.FindKey("installedby")->GetString()); + } +} + +TEST_P(UpdateCheckerTest, ComponentDisabled) { + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + auto& component = components[kUpdateItemId]; + auto crx_component = component->crx_component(); + + { + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_FALSE(app.FindKey("disabled")); + } + + { + crx_component->disabled_reasons = {}; + component->set_crx_component(*crx_component); + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_FALSE(app.FindKey("disabled")); + } + + { + crx_component->disabled_reasons = {0}; + component->set_crx_component(*crx_component); + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(false, app.FindKey("enabled")->GetBool()); + const auto& disabled = app.FindKey("disabled")->GetList(); + EXPECT_EQ(1u, disabled.size()); + EXPECT_EQ(0, disabled[0].FindKey("reason")->GetInt()); + } + { + crx_component->disabled_reasons = {1}; + component->set_crx_component(*crx_component); + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(false, app.FindKey("enabled")->GetBool()); + const auto& disabled = app.FindKey("disabled")->GetList(); + EXPECT_EQ(1u, disabled.size()); + EXPECT_EQ(1, disabled[0].FindKey("reason")->GetInt()); + } + + { + crx_component->disabled_reasons = {4, 8, 16}; + component->set_crx_component(*crx_component); + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(false, app.FindKey("enabled")->GetBool()); + const auto& disabled = app.FindKey("disabled")->GetList(); + EXPECT_EQ(3u, disabled.size()); + EXPECT_EQ(4, disabled[0].FindKey("reason")->GetInt()); + EXPECT_EQ(8, disabled[1].FindKey("reason")->GetInt()); + EXPECT_EQ(16, disabled[2].FindKey("reason")->GetInt()); + } + + { + crx_component->disabled_reasons = {0, 4, 8, 16}; + component->set_crx_component(*crx_component); + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(false, app.FindKey("enabled")->GetBool()); + const auto& disabled = app.FindKey("disabled")->GetList(); + EXPECT_EQ(4u, disabled.size()); + EXPECT_EQ(0, disabled[0].FindKey("reason")->GetInt()); + EXPECT_EQ(4, disabled[1].FindKey("reason")->GetInt()); + EXPECT_EQ(8, disabled[2].FindKey("reason")->GetInt()); + EXPECT_EQ(16, disabled[3].FindKey("reason")->GetInt()); + } +} + +TEST_P(UpdateCheckerTest, UpdateCheckUpdateDisabled) { + config_->SetBrand(""); + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + auto& component = components[kUpdateItemId]; + auto crx_component = component->crx_component(); + + // Ignore this test parameter to keep the test simple. + update_context_->is_foreground = false; + { + // Tests the scenario where: + // * the component does not support group policies. + // * the component updates are disabled. + // Expects the group policy to be ignored and the update check to not + // include the "updatedisabled" attribute. + EXPECT_FALSE(crx_component->supports_group_policy_enable_component_updates); + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString()); + EXPECT_EQ("0.9", app.FindKey("version")->GetString()); + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_TRUE(app.FindKey("updatecheck")->DictEmpty()); + } + { + // Tests the scenario where: + // * the component supports group policies. + // * the component updates are disabled. + // Expects the update check to include the "updatedisabled" attribute. + crx_component->supports_group_policy_enable_component_updates = true; + component->set_crx_component(*crx_component); + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, false, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString()); + EXPECT_EQ("0.9", app.FindKey("version")->GetString()); + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_TRUE(app.FindPath({"updatecheck", "updatedisabled"})->GetBool()); + } + { + // Tests the scenario where: + // * the component does not support group policies. + // * the component updates are enabled. + // Expects the update check to not include the "updatedisabled" attribute. + crx_component->supports_group_policy_enable_component_updates = false; + component->set_crx_component(*crx_component); + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString()); + EXPECT_EQ("0.9", app.FindKey("version")->GetString()); + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_TRUE(app.FindKey("updatecheck")->DictEmpty()); + } + { + // Tests the scenario where: + // * the component supports group policies. + // * the component updates are enabled. + // Expects the update check to not include the "updatedisabled" attribute. + crx_component->supports_group_policy_enable_component_updates = true; + component->set_crx_component(*crx_component); + auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>( + config_->test_url_loader_factory()); + EXPECT_TRUE(post_interceptor->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + const auto& request = post_interceptor->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString()); + EXPECT_EQ("0.9", app.FindKey("version")->GetString()); + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_TRUE(app.FindKey("updatecheck")->DictEmpty()); + } +} + +TEST_P(UpdateCheckerTest, NoUpdateActionRun) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_noupdate.json"))); + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(1, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + ASSERT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + // Sanity check the arguments of the callback after parsing. + EXPECT_EQ(ErrorCategory::kNone, error_category_); + EXPECT_EQ(0, error_); + EXPECT_TRUE(results_); + EXPECT_EQ(1u, results_->list.size()); + const auto& result = results_->list.front(); + EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", result.extension_id.c_str()); + EXPECT_STREQ("noupdate", result.status.c_str()); + EXPECT_STREQ("this", result.action_run.c_str()); +} + +TEST_P(UpdateCheckerTest, UpdatePauseResume) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_noupdate.json"))); + post_interceptor_->url_job_request_ready_callback(base::BindOnce( + [](URLLoaderPostInterceptor* post_interceptor) { + post_interceptor->Resume(); + }, + post_interceptor_.get())); + post_interceptor_->Pause(); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + // Ignore this test parameter to keep the test simple. + update_context_->is_foreground = false; + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + const auto& request = post_interceptor_->GetRequestBody(0); + const auto root = base::JSONReader::Read(request); + ASSERT_TRUE(root); + const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0]; + EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString()); + EXPECT_EQ("0.9", app.FindKey("version")->GetString()); + EXPECT_EQ("TEST", app.FindKey("brand")->GetString()); + EXPECT_EQ(true, app.FindKey("enabled")->GetBool()); + EXPECT_TRUE(app.FindKey("updatecheck")->DictEmpty()); + EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt()); + EXPECT_EQ("fp1", app.FindKey("packages") + ->FindKey("package") + ->GetList()[0] + .FindKey("fp") + ->GetString()); +} + +// Tests that an update checker object and its underlying SimpleURLLoader can +// be safely destroyed while it is paused. +TEST_P(UpdateCheckerTest, UpdateResetUpdateChecker) { + base::RunLoop runloop; + auto quit_closure = runloop.QuitClosure(); + + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_1.json"))); + post_interceptor_->url_job_request_ready_callback(base::BindOnce( + [](base::OnceClosure quit_closure) { std::move(quit_closure).Run(); }, + std::move(quit_closure))); + post_interceptor_->Pause(); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + runloop.Run(); +} + +// The update response contains a protocol version which does not match the +// expected protocol version. +TEST_P(UpdateCheckerTest, ParseErrorProtocolVersionMismatch) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_parse_error.json"))); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(1, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + ASSERT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_); + EXPECT_EQ(-10003, error_); + EXPECT_FALSE(results_); +} + +// The update response contains a status |error-unknownApplication| for the +// app. The response is succesfully parsed and a result is extracted to +// indicate this status. +TEST_P(UpdateCheckerTest, ParseErrorAppStatusErrorUnknownApplication) { + EXPECT_TRUE(post_interceptor_->ExpectRequest( + std::make_unique<PartialMatch>("updatecheck"), + test_file("updatecheck_reply_unknownapp.json"))); + + update_checker_ = UpdateChecker::Create(config_, metadata_.get()); + + IdToComponentPtrMap components; + components[kUpdateItemId] = MakeComponent(); + + update_checker_->CheckForUpdates( + update_context_->session_id, {kUpdateItemId}, components, {}, true, + base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, + base::Unretained(this))); + RunThreads(); + + EXPECT_EQ(1, post_interceptor_->GetHitCount()) + << post_interceptor_->GetRequestsAsString(); + ASSERT_EQ(1, post_interceptor_->GetCount()) + << post_interceptor_->GetRequestsAsString(); + + EXPECT_EQ(ErrorCategory::kNone, error_category_); + EXPECT_EQ(0, error_); + EXPECT_TRUE(results_); + EXPECT_EQ(1u, results_->list.size()); + const auto& result = results_->list.front(); + EXPECT_STREQ("error-unknownApplication", result.status.c_str()); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/update_client.cc b/src/updater_components/update_client/update_client.cc new file mode 100644 index 0000000..1a12e47 --- /dev/null +++ b/src/updater_components/update_client/update_client.cc
@@ -0,0 +1,254 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/update_client.h" + +#include <algorithm> +#include <queue> +#include <set> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/observer_list.h" +#include "base/stl_util.h" +#include "base/threading/thread_checker.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/crx_file/crx_verifier.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/update_client/configurator.h" +#include "components/update_client/crx_update_item.h" +#include "components/update_client/persisted_data.h" +#include "components/update_client/ping_manager.h" +#include "components/update_client/protocol_parser.h" +#include "components/update_client/task_send_uninstall_ping.h" +#include "components/update_client/task_update.h" +#include "components/update_client/update_checker.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/update_client_internal.h" +#include "components/update_client/update_engine.h" +#include "components/update_client/utils.h" +#include "url/gurl.h" + +namespace update_client { + +CrxUpdateItem::CrxUpdateItem() : state(ComponentState::kNew) {} +CrxUpdateItem::~CrxUpdateItem() = default; +CrxUpdateItem::CrxUpdateItem(const CrxUpdateItem& other) = default; + +CrxComponent::CrxComponent() + : allows_background_download(true), + requires_network_encryption(true), + crx_format_requirement( + crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF), + supports_group_policy_enable_component_updates(false) {} +CrxComponent::CrxComponent(const CrxComponent& other) = default; +CrxComponent::~CrxComponent() = default; + +// It is important that an instance of the UpdateClient binds an unretained +// pointer to itself. Otherwise, a life time circular dependency between this +// instance and its inner members prevents the destruction of this instance. +// Using unretained references is allowed in this case since the life time of +// the UpdateClient instance exceeds the life time of its inner members, +// including any thread objects that might execute callbacks bound to it. +UpdateClientImpl::UpdateClientImpl( + scoped_refptr<Configurator> config, + scoped_refptr<PingManager> ping_manager, + UpdateChecker::Factory update_checker_factory, + CrxDownloader::Factory crx_downloader_factory) + : is_stopped_(false), + config_(config), + ping_manager_(ping_manager), + update_engine_(base::MakeRefCounted<UpdateEngine>( + config, + update_checker_factory, + crx_downloader_factory, + ping_manager_.get(), + base::Bind(&UpdateClientImpl::NotifyObservers, + base::Unretained(this)))) {} + +UpdateClientImpl::~UpdateClientImpl() { + DCHECK(thread_checker_.CalledOnValidThread()); + + DCHECK(task_queue_.empty()); + DCHECK(tasks_.empty()); + + config_ = nullptr; +} + +void UpdateClientImpl::Install(const std::string& id, + CrxDataCallback crx_data_callback, + Callback callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (IsUpdating(id)) { + std::move(callback).Run(Error::UPDATE_IN_PROGRESS); + return; + } + + std::vector<std::string> ids = {id}; + + // Install tasks are run concurrently and never queued up. They are always + // considered foreground tasks. + constexpr bool kIsForeground = true; + RunTask(base::MakeRefCounted<TaskUpdate>( + update_engine_.get(), kIsForeground, ids, std::move(crx_data_callback), + base::BindOnce(&UpdateClientImpl::OnTaskComplete, this, + std::move(callback)))); +} + +void UpdateClientImpl::Update(const std::vector<std::string>& ids, + CrxDataCallback crx_data_callback, + bool is_foreground, + Callback callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + auto task = base::MakeRefCounted<TaskUpdate>( + update_engine_.get(), is_foreground, ids, std::move(crx_data_callback), + base::BindOnce(&UpdateClientImpl::OnTaskComplete, this, + std::move(callback))); + + // If no other tasks are running at the moment, run this update task. + // Otherwise, queue the task up. + if (tasks_.empty()) { + RunTask(task); + } else { + task_queue_.push_back(task); + } +} + +void UpdateClientImpl::RunTask(scoped_refptr<Task> task) { + DCHECK(thread_checker_.CalledOnValidThread()); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&Task::Run, base::Unretained(task.get()))); + tasks_.insert(task); +} + +void UpdateClientImpl::OnTaskComplete(Callback callback, + scoped_refptr<Task> task, + Error error) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(task); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), error)); + + // Remove the task from the set of the running tasks. Only tasks handled by + // the update engine can be in this data structure. + tasks_.erase(task); + + if (is_stopped_) + return; + + // Pick up a task from the queue if the queue has pending tasks and no other + // task is running. + if (tasks_.empty() && !task_queue_.empty()) { + auto task = task_queue_.front(); + task_queue_.pop_front(); + RunTask(task); + } +} + +void UpdateClientImpl::AddObserver(Observer* observer) { + DCHECK(thread_checker_.CalledOnValidThread()); + observer_list_.AddObserver(observer); +} + +void UpdateClientImpl::RemoveObserver(Observer* observer) { + DCHECK(thread_checker_.CalledOnValidThread()); + observer_list_.RemoveObserver(observer); +} + +void UpdateClientImpl::NotifyObservers(Observer::Events event, + const std::string& id) { + DCHECK(thread_checker_.CalledOnValidThread()); + for (auto& observer : observer_list_) + observer.OnEvent(event, id); +} + +bool UpdateClientImpl::GetCrxUpdateState(const std::string& id, + CrxUpdateItem* update_item) const { + return update_engine_->GetUpdateState(id, update_item); +} + +bool UpdateClientImpl::IsUpdating(const std::string& id) const { + DCHECK(thread_checker_.CalledOnValidThread()); + + for (const auto task : tasks_) { + const auto ids = task->GetIds(); + if (base::Contains(ids, id)) { + return true; + } + } + + for (const auto task : task_queue_) { + const auto ids = task->GetIds(); + if (base::Contains(ids, id)) { + return true; + } + } + + return false; +} + +void UpdateClientImpl::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + + is_stopped_ = true; + + // In the current implementation it is sufficient to cancel the pending + // tasks only. The tasks that are run by the update engine will stop + // making progress naturally, as the main task runner stops running task + // actions. Upon the browser shutdown, the resources employed by the active + // tasks will leak, as the operating system kills the thread associated with + // the update engine task runner. Further refactoring may be needed in this + // area, to cancel the running tasks by canceling the current action update. + // This behavior would be expected, correct, and result in no resource leaks + // in all cases, in shutdown or not. + // + // Cancel the pending tasks. These tasks are safe to cancel and delete since + // they have not picked up by the update engine, and not shared with any + // task runner yet. + while (!task_queue_.empty()) { + auto task = task_queue_.front(); + task_queue_.pop_front(); + task->Cancel(); + } +} + +void UpdateClientImpl::SendUninstallPing(const std::string& id, + const base::Version& version, + int reason, + Callback callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + RunTask(base::MakeRefCounted<TaskSendUninstallPing>( + update_engine_.get(), id, version, reason, + base::BindOnce(&UpdateClientImpl::OnTaskComplete, base::Unretained(this), + std::move(callback)))); +} + +scoped_refptr<UpdateClient> UpdateClientFactory( + scoped_refptr<Configurator> config) { + return base::MakeRefCounted<UpdateClientImpl>( + config, base::MakeRefCounted<PingManager>(config), &UpdateChecker::Create, + &CrxDownloader::Create); +} + +void RegisterPrefs(PrefRegistrySimple* registry) { + PersistedData::RegisterPrefs(registry); +} + +// This function has the exact same implementation as RegisterPrefs. We have +// this implementation here to make the intention more clear that is local user +// profile access is needed. +void RegisterProfilePrefs(PrefRegistrySimple* registry) { + PersistedData::RegisterPrefs(registry); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/update_client.h b/src/updater_components/update_client/update_client.h new file mode 100644 index 0000000..6d58c59 --- /dev/null +++ b/src/updater_components/update_client/update_client.h
@@ -0,0 +1,417 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_H_ +#define COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_H_ + +#include <stdint.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/callback_forward.h" +#include "base/memory/ref_counted.h" +#include "base/optional.h" +#include "base/version.h" +#include "components/update_client/update_client_errors.h" + +// The UpdateClient class is a facade with a simple interface. The interface +// exposes a few APIs to install a CRX or update a group of CRXs. +// +// The difference between a CRX install and a CRX update is relatively minor. +// The terminology going forward will use the word "update" to cover both +// install and update scenarios, except where details regarding the install +// case are relevant. +// +// Handling an update consists of a series of actions such as sending an update +// check to the server, followed by parsing the server response, identifying +// the CRXs that require an update, downloading the differential update if +// it is available, unpacking and patching the differential update, then +// falling back to trying a similar set of actions using the full update. +// At the end of this process, completion pings are sent to the server, +// as needed, for the CRXs which had updates. +// +// As a general idea, this code handles the action steps needed to update +// a group of components serially, one step at a time. However, concurrent +// execution of calls to UpdateClient::Update is possible, therefore, +// queuing of updates could happen in some cases. More below. +// +// The UpdateClient class features a subject-observer interface to observe +// the CRX state changes during an update. +// +// The threading model for this code assumes that most of the code in the +// public interface runs on a SingleThreadTaskRunner. +// This task runner corresponds to the browser UI thread in many cases. There +// are parts of the installer interface that run on blocking task runners, which +// are usually threads in a thread pool. +// +// Using the UpdateClient is relatively easy. This assumes that the client +// of this code has already implemented the observer interface as needed, and +// can provide an installer, as described below. +// +// std::unique_ptr<UpdateClient> update_client(UpdateClientFactory(...)); +// update_client->AddObserver(&observer); +// std::vector<std::string> ids; +// ids.push_back(...)); +// update_client->Update(ids, base::BindOnce(...), base::BindOnce(...)); +// +// UpdateClient::Update takes two callbacks as parameters. First callback +// allows the client of this code to provide an instance of CrxComponent +// data structure that specifies additional parameters of the update. +// CrxComponent has a CrxInstaller data member, which must be provided by the +// callers of this class. The second callback indicates that this non-blocking +// call has completed. +// +// There could be several ways of triggering updates for a CRX, user-initiated, +// or timer-based. Since the execution of updates is concurrent, the parameters +// for the update must be provided right before the update is handled. +// Otherwise, the version of the CRX set in the CrxComponent may not be correct. +// +// The UpdateClient public interface includes two functions: Install and +// Update. These functions correspond to installing one CRX immediately as a +// foreground activity (Install), and updating a group of CRXs silently in the +// background (Update). This distinction is important. Background updates are +// queued up and their actions run serially, one at a time, for the purpose of +// conserving local resources such as CPU, network, and I/O. +// On the other hand, installs are never queued up but run concurrently, as +// requested by the user. +// +// The update client introduces a runtime constraint regarding interleaving +// updates and installs. If installs or updates for a given CRX are in progress, +// then installs for the same CRX will fail with a specific error. +// +// Implementation details. +// +// The implementation details below are not relevant to callers of this +// code. However, these design notes are relevant to the owners and maintainers +// of this module. +// +// The design for the update client consists of a number of abstractions +// such as: task, update engine, update context, and action. +// The execution model for these abstractions is simple. They usually expose +// a public, non-blocking Run function, and they invoke a callback when +// the Run function has completed. +// +// A task is the unit of work for the UpdateClient. A task is associated +// with a single call of the Update function. A task represents a group +// of CRXs that are updated together. +// +// The UpdateClient is responsible for the queuing of tasks, if queuing is +// needed. +// +// When the task runs, it calls the update engine to handle the updates for +// the CRXs associated with the task. The UpdateEngine is the abstraction +// responsible for breaking down the update in a set of discrete steps, which +// are implemented as actions, and running the actions. +// +// The UpdateEngine maintains a set of UpdateContext instances. Each of +// these instances maintains the update state for all the CRXs belonging to +// a given task. The UpdateContext contains a queue of CRX ids. +// The UpdateEngine will handle updates for the CRXs in the order they appear +// in the queue, until the queue is empty. +// +// The update state for each CRX is maintained in a container of CrxUpdateItem*. +// As actions run, each action updates the CRX state, represented by one of +// these CrxUpdateItem* instances. +// +// Although the UpdateEngine can and will run update tasks concurrently, the +// actions of a task are run sequentially. +// +// The Action is a polymorphic type. There is some code reuse for convenience, +// implemented as a mixin. The polymorphic behavior of some of the actions +// is achieved using a template method. +// +// State changes of a CRX could generate events, which are observed using a +// subject-observer interface. +// +// The actions chain up. In some sense, the actions implement a state machine, +// as the CRX undergoes a series of state transitions in the process of +// being checked for updates and applying the update. + +class PrefRegistrySimple; + +namespace base { +class FilePath; +} + +namespace crx_file { +enum class VerifierFormat; +} + +namespace update_client { + +class Configurator; +enum class Error; +struct CrxUpdateItem; + +enum class ComponentState { + kNew, + kChecking, + kCanUpdate, + kDownloadingDiff, + kDownloading, + kDownloaded, + kUpdatingDiff, + kUpdating, + kUpdated, + kUpToDate, + kUpdateError, + kUninstalled, + kRun, + kLastStatus +}; + +// Defines an interface for a generic CRX installer. +class CrxInstaller : public base::RefCountedThreadSafe<CrxInstaller> { + public: + // Contains the result of the Install operation. + struct Result { + explicit Result(int error, int extended_error = 0) + : error(error), extended_error(extended_error) {} + explicit Result(InstallError error, int extended_error = 0) + : error(static_cast<int>(error)), extended_error(extended_error) {} + int error = 0; // 0 indicates that install has been successful. + int extended_error = 0; + }; + + using Callback = base::OnceCallback<void(const Result& result)>; + + // Called on the main thread when there was a problem unpacking or + // verifying the CRX. |error| is a non-zero value which is only meaningful + // to the caller. + virtual void OnUpdateError(int error) = 0; + + // Called by the update service when a CRX has been unpacked + // and it is ready to be installed. |unpack_path| contains the + // temporary directory with all the unpacked CRX files. |pubkey| contains the + // public key of the CRX in the PEM format, without the header and the footer. + // The caller must invoke the |callback| when the install flow has completed. + // This method may be called from a thread other than the main thread. + virtual void Install(const base::FilePath& unpack_path, + const std::string& public_key, + Callback callback) = 0; + + // Sets |installed_file| to the full path to the installed |file|. |file| is + // the filename of the file in this CRX. Returns false if this is + // not possible (the file has been removed or modified, or its current + // location is unknown). Otherwise, it returns true. + virtual bool GetInstalledFile(const std::string& file, + base::FilePath* installed_file) = 0; + + // Called when a CRX has been unregistered and all versions should + // be uninstalled from disk. Returns true if uninstallation is supported, + // and false otherwise. + virtual bool Uninstall() = 0; + + protected: + friend class base::RefCountedThreadSafe<CrxInstaller>; + + virtual ~CrxInstaller() {} +}; + +// A dictionary of installer-specific, arbitrary name-value pairs, which +// may be used in the update checks requests. +using InstallerAttributes = std::map<std::string, std::string>; + +struct CrxComponent { + CrxComponent(); + CrxComponent(const CrxComponent& other); + ~CrxComponent(); + + // SHA256 hash of the CRX's public key. + std::vector<uint8_t> pk_hash; + scoped_refptr<CrxInstaller> installer; + + // The current version if the CRX is updated. Otherwise, "0" or "0.0" if + // the CRX is installed. + base::Version version; + + std::string fingerprint; // Optional. + std::string name; // Optional. + std::vector<std::string> handled_mime_types; + + // Optional. + // Valid values for the name part of an attribute match + // ^[-_a-zA-Z0-9]{1,256}$ and valid values the value part of an attribute + // match ^[-.,;+_=a-zA-Z0-9]{0,256}$ . + InstallerAttributes installer_attributes; + + // Specifies that the CRX can be background-downloaded in some cases. + // The default for this value is |true|. + bool allows_background_download; + + // Specifies that the update checks and pings associated with this component + // require confidentiality. The default for this value is |true|. As a side + // note, the confidentiality of the downloads is enforced by the server, + // which only returns secure download URLs in this case. + bool requires_network_encryption; + + // Specifies the strength of package validation required for the item. + crx_file::VerifierFormat crx_format_requirement; + + // True if the component allows enabling or disabling updates by group policy. + // This member should be set to |false| for data, non-binary components, such + // as CRLSet, Supervised User Whitelists, STH Set, Origin Trials, and File + // Type Policies. + bool supports_group_policy_enable_component_updates; + + // Reasons why this component/extension is disabled. + std::vector<int> disabled_reasons; + + // Information about where the component/extension was installed from. + // For extension, this information is set from the update service, which + // gets the install source from the update URL. + std::string install_source; + + // Information about where the component/extension was loaded from. + // For extensions, this information is inferred from the extension + // registry. + std::string install_location; +}; + +// Called when a non-blocking call of UpdateClient completes. +using Callback = base::OnceCallback<void(Error error)>; + +// All methods are safe to call only from the browser's main thread. Once an +// instance of this class is created, the reference to it must be released +// only after the thread pools of the browser process have been destroyed and +// the browser process has gone single-threaded. +class UpdateClient : public base::RefCounted<UpdateClient> { + public: + using CrxDataCallback = + base::OnceCallback<std::vector<base::Optional<CrxComponent>>( + const std::vector<std::string>& ids)>; + + // Defines an interface to observe the UpdateClient. It provides + // notifications when state changes occur for the service itself or for the + // registered CRXs. + class Observer { + public: + enum class Events { + // Sent before the update client does an update check. + COMPONENT_CHECKING_FOR_UPDATES = 1, + + // Sent when there is a new version of a registered CRX. After + // the notification is sent the CRX will be downloaded unless the + // update client inserts a + COMPONENT_UPDATE_FOUND, + + // Sent when a CRX is in the update queue but it can't be acted on + // right away, because the update client spaces out CRX updates due to a + // throttling policy. + COMPONENT_WAIT, + + // Sent after the new CRX has been downloaded but before the install + // or the upgrade is attempted. + COMPONENT_UPDATE_READY, + + // Sent when a CRX has been successfully updated. + COMPONENT_UPDATED, + + // Sent when a CRX has not been updated because there was no update + // available for this component. + COMPONENT_NOT_UPDATED, + + // Sent when an error ocurred during an update for any reason, including + // the update check itself failed, or the download of the update payload + // failed, or applying the update failed. + COMPONENT_UPDATE_ERROR, + + // Sent when CRX bytes are being downloaded. + COMPONENT_UPDATE_DOWNLOADING, + }; + + virtual ~Observer() {} + + // Called by the update client when a state change happens. + // If an |id| is specified, then the event is fired on behalf of the + // specific CRX. The implementors of this interface are + // expected to filter the relevant events based on the id of the CRX. + virtual void OnEvent(Events event, const std::string& id) = 0; + }; + + // Adds an observer for this class. An observer should not be added more + // than once. The caller retains the ownership of the observer object. + virtual void AddObserver(Observer* observer) = 0; + + // Removes an observer. It is safe for an observer to be removed while + // the observers are being notified. + virtual void RemoveObserver(Observer* observer) = 0; + + // Installs the specified CRX. Calls back on |callback| after the + // update has been handled. The |error| parameter of the |callback| + // contains an error code in the case of a run-time error, or 0 if the + // install has been handled successfully. Overlapping calls of this function + // are executed concurrently, as long as the id parameter is different, + // meaning that installs of different components are parallelized. + // The |Install| function is intended to be used for foreground installs of + // one CRX. These cases are usually associated with on-demand install + // scenarios, which are triggered by user actions. Installs are never + // queued up. + virtual void Install(const std::string& id, + CrxDataCallback crx_data_callback, + Callback callback) = 0; + + // Updates the specified CRXs. Calls back on |crx_data_callback| before the + // update is attempted to give the caller the opportunity to provide the + // instances of CrxComponent to be used for this update. The |Update| function + // is intended to be used for background updates of several CRXs. Overlapping + // calls to this function result in a queuing behavior, and the execution + // of each call is serialized. In addition, updates are always queued up when + // installs are running. The |is_foreground| parameter must be set to true if + // the invocation of this function is a result of a user initiated update. + virtual void Update(const std::vector<std::string>& ids, + CrxDataCallback crx_data_callback, + bool is_foreground, + Callback callback) = 0; + + // Sends an uninstall ping for the CRX identified by |id| and |version|. The + // |reason| parameter is defined by the caller. The current implementation of + // this function only sends a best-effort, fire-and-forget ping. It has no + // other side effects regarding installs or updates done through an instance + // of this class. + virtual void SendUninstallPing(const std::string& id, + const base::Version& version, + int reason, + Callback callback) = 0; + + // Returns status details about a CRX update. The function returns true in + // case of success and false in case of errors, such as |id| was + // invalid or not known. + virtual bool GetCrxUpdateState(const std::string& id, + CrxUpdateItem* update_item) const = 0; + + // Returns true if the |id| is found in any running task. + virtual bool IsUpdating(const std::string& id) const = 0; + + // Cancels the queued updates and makes a best effort to stop updates in + // progress as soon as possible. Some updates may not be stopped, in which + // case, the updates will run to completion. Calling this function has no + // effect if updates are not currently executed or queued up. + virtual void Stop() = 0; + + protected: + friend class base::RefCounted<UpdateClient>; + + virtual ~UpdateClient() {} +}; + +// Creates an instance of the update client. +scoped_refptr<UpdateClient> UpdateClientFactory( + scoped_refptr<Configurator> config); + +// This must be called prior to the construction of any Configurator that +// contains a PrefService. +void RegisterPrefs(PrefRegistrySimple* registry); + +// This must be called prior to the construction of any Configurator that +// needs access to local user profiles. +// This function is mostly used for ExtensionUpdater, which requires update +// info from user profiles. +void RegisterProfilePrefs(PrefRegistrySimple* registry); + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_H_
diff --git a/src/updater_components/update_client/update_client_errors.h b/src/updater_components/update_client/update_client_errors.h new file mode 100644 index 0000000..3ee0509 --- /dev/null +++ b/src/updater_components/update_client/update_client_errors.h
@@ -0,0 +1,118 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_ERRORS_H_ +#define COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_ERRORS_H_ + +namespace update_client { + +// Errors generated as a result of calling UpdateClient member functions. +// These errors are not sent in pings. +enum class Error { + NONE = 0, + UPDATE_IN_PROGRESS = 1, + UPDATE_CANCELED = 2, + RETRY_LATER = 3, + SERVICE_ERROR = 4, + UPDATE_CHECK_ERROR = 5, + CRX_NOT_FOUND = 6, + INVALID_ARGUMENT = 7, + MAX_VALUE, +}; + +// These errors are sent in pings. Add new values only to the bottom of +// the enums below; the order must be kept stable. +enum class ErrorCategory { + kNone = 0, + kDownload, + kUnpack, + kInstall, + kService, // Runtime errors which occur in the service itself. + kUpdateCheck, +}; + +// These errors are returned with the |kNetworkError| error category. This +// category could include other errors such as the errors defined by +// the Chrome net stack. +enum class CrxDownloaderError { + NONE = 0, + NO_URL = 10, + NO_HASH = 11, + BAD_HASH = 12, // The downloaded file fails the hash verification. + // The Windows BITS queue contains to many update client jobs. The value is + // chosen so that it can be reported as a custom COM error on this platform. + BITS_TOO_MANY_JOBS = 0x0200, + GENERIC_ERROR = -1 +}; + +// These errors are returned with the |kUnpack| error category and +// indicate unpacker or patcher error. +enum class UnpackerError { + kNone = 0, + kInvalidParams = 1, + kInvalidFile = 2, + kUnzipPathError = 3, + kUnzipFailed = 4, + // kNoManifest = 5, // Deprecated. Never used. + kBadManifest = 6, + kBadExtension = 7, + // kInvalidId = 8, // Deprecated. Combined with kInvalidFile. + // kInstallerError = 9, // Deprecated. Don't use. + kIoError = 10, + kDeltaVerificationFailure = 11, + kDeltaBadCommands = 12, + kDeltaUnsupportedCommand = 13, + kDeltaOperationFailure = 14, + kDeltaPatchProcessFailure = 15, + kDeltaMissingExistingFile = 16, + // kFingerprintWriteFailed = 17, // Deprecated. Don't use. +}; + +// These errors are returned with the |kService| error category and +// are returned by the component installers. +enum class InstallError { + NONE = 0, + FINGERPRINT_WRITE_FAILED = 2, + BAD_MANIFEST = 3, + GENERIC_ERROR = 9, // Matches kInstallerError for compatibility. + MOVE_FILES_ERROR = 10, + SET_PERMISSIONS_FAILED = 11, + INVALID_VERSION = 12, + VERSION_NOT_UPGRADED = 13, + NO_DIR_COMPONENT_USER = 14, + CLEAN_INSTALL_DIR_FAILED = 15, + INSTALL_VERIFICATION_FAILED = 16, + CUSTOM_ERROR_BASE = 100, // Specific installer errors go above this value. +}; + +// These errors are returned with the |kService| error category and +// indicate critical or configuration errors in the update service. +enum class ServiceError { + NONE = 0, + SERVICE_WAIT_FAILED = 1, + UPDATE_DISABLED = 2, +}; + +// These errors are related to serialization, deserialization, and parsing of +// protocol requests. +// The begin value for this enum is chosen not to conflict with network errors +// defined by net/base/net_error_list.h. The callers don't have to handle this +// error in any meaningful way, but this value may be reported in UMA stats, +// therefore avoiding collisions with known network errors is desirable. +enum class ProtocolError : int { + NONE = 0, + RESPONSE_NOT_TRUSTED = -10000, + MISSING_PUBLIC_KEY = -10001, + MISSING_URLS = -10002, + PARSE_FAILED = -10003, + UPDATE_RESPONSE_NOT_FOUND = -10004, + URL_FETCHER_FAILED = -10005, + UNKNOWN_APPLICATION = -10006, + RESTRICTED_APPLICATION = -10007, + INVALID_APPID = -10008, +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_ERRORS_H_
diff --git a/src/updater_components/update_client/update_client_internal.h b/src/updater_components/update_client/update_client_internal.h new file mode 100644 index 0000000..2b6158c --- /dev/null +++ b/src/updater_components/update_client/update_client_internal.h
@@ -0,0 +1,92 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_INTERNAL_H_ +#define COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_INTERNAL_H_ + +#include <memory> +#include <set> +#include <string> +#include <vector> + +#include "base/containers/circular_deque.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "base/threading/thread_checker.h" +#include "components/update_client/crx_downloader.h" +#include "components/update_client/update_checker.h" +#include "components/update_client/update_client.h" + +namespace update_client { + +class Configurator; +class PingManager; +class Task; +class UpdateEngine; +enum class Error; + +class UpdateClientImpl : public UpdateClient { + public: + UpdateClientImpl(scoped_refptr<Configurator> config, + scoped_refptr<PingManager> ping_manager, + UpdateChecker::Factory update_checker_factory, + CrxDownloader::Factory crx_downloader_factory); + + // Overrides for UpdateClient. + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + void Install(const std::string& id, + CrxDataCallback crx_data_callback, + Callback callback) override; + void Update(const std::vector<std::string>& ids, + CrxDataCallback crx_data_callback, + bool is_foreground, + Callback callback) override; + bool GetCrxUpdateState(const std::string& id, + CrxUpdateItem* update_item) const override; + bool IsUpdating(const std::string& id) const override; + void Stop() override; + void SendUninstallPing(const std::string& id, + const base::Version& version, + int reason, + Callback callback) override; + + private: + ~UpdateClientImpl() override; + + void RunTask(scoped_refptr<Task> task); + void OnTaskComplete(Callback callback, scoped_refptr<Task> task, Error error); + + void NotifyObservers(Observer::Events event, const std::string& id); + + base::ThreadChecker thread_checker_; + + // True if Stop method has been called. + bool is_stopped_ = false; + + scoped_refptr<Configurator> config_; + + // Contains the tasks that are pending. In the current implementation, + // only update tasks (background tasks) are queued up. These tasks are + // pending while they are in this queue. They have not been picked up yet + // by the update engine. + base::circular_deque<scoped_refptr<Task>> task_queue_; + + // Contains all tasks in progress. These are the tasks that the update engine + // is executing at one moment. Install tasks are run concurrently, update + // tasks are always serialized, and update tasks are queued up if install + // tasks are running. In addition, concurrent install tasks for the same id + // are not allowed. + std::set<scoped_refptr<Task>> tasks_; + scoped_refptr<PingManager> ping_manager_; + scoped_refptr<UpdateEngine> update_engine_; + base::ObserverList<Observer>::Unchecked observer_list_; + + DISALLOW_COPY_AND_ASSIGN(UpdateClientImpl); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_INTERNAL_H_
diff --git a/src/updater_components/update_client/update_client_unittest.cc b/src/updater_components/update_client/update_client_unittest.cc new file mode 100644 index 0000000..2a614f4 --- /dev/null +++ b/src/updater_components/update_client/update_client_unittest.cc
@@ -0,0 +1,3933 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/containers/flat_map.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/location.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/optional.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/stl_util.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#include "base/test/scoped_path_override.h" +#include "base/test/task_environment.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/values.h" +#include "base/version.h" +#include "build/build_config.h" +#include "components/crx_file/crx_verifier.h" +#include "components/prefs/testing_pref_service.h" +#include "components/update_client/component_unpacker.h" +#include "components/update_client/crx_update_item.h" +#include "components/update_client/network.h" +#include "components/update_client/patcher.h" +#include "components/update_client/persisted_data.h" +#include "components/update_client/ping_manager.h" +#include "components/update_client/protocol_handler.h" +#include "components/update_client/test_configurator.h" +#include "components/update_client/test_installer.h" +#include "components/update_client/unzipper.h" +#include "components/update_client/update_checker.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/update_client_internal.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace update_client { + +namespace { + +using base::FilePath; + +// Makes a copy of the file specified by |from_path| in a temporary directory +// and returns the path of the copy. Returns true if successful. Cleans up if +// there was an error creating the copy. +bool MakeTestFile(const FilePath& from_path, FilePath* to_path) { + FilePath temp_dir; + bool result = + CreateNewTempDirectory(FILE_PATH_LITERAL("update_client"), &temp_dir); + if (!result) + return false; + + FilePath temp_file; + result = CreateTemporaryFileInDir(temp_dir, &temp_file); + if (!result) + return false; + + result = CopyFile(from_path, temp_file); + if (!result) { + DeleteFile(temp_file, false); + return false; + } + + *to_path = temp_file; + return true; +} + +using Events = UpdateClient::Observer::Events; + +class MockObserver : public UpdateClient::Observer { + public: + MOCK_METHOD2(OnEvent, void(Events event, const std::string&)); +}; + +} // namespace + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::AnyNumber; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Mock; +using ::testing::Return; + +using std::string; + +class MockPingManagerImpl : public PingManager { + public: + struct PingData { + std::string id; + base::Version previous_version; + base::Version next_version; + ErrorCategory error_category = ErrorCategory::kNone; + int error_code = 0; + int extra_code1 = 0; + ErrorCategory diff_error_category = ErrorCategory::kNone; + int diff_error_code = 0; + bool diff_update_failed = false; + }; + + explicit MockPingManagerImpl(scoped_refptr<Configurator> config); + + void SendPing(const Component& component, Callback callback) override; + + const std::vector<PingData>& ping_data() const; + + const std::vector<base::Value>& events() const; + + protected: + ~MockPingManagerImpl() override; + + private: + std::vector<PingData> ping_data_; + std::vector<base::Value> events_; + DISALLOW_COPY_AND_ASSIGN(MockPingManagerImpl); +}; + +MockPingManagerImpl::MockPingManagerImpl(scoped_refptr<Configurator> config) + : PingManager(config) {} + +MockPingManagerImpl::~MockPingManagerImpl() {} + +void MockPingManagerImpl::SendPing(const Component& component, + Callback callback) { + PingData ping_data; + ping_data.id = component.id_; + ping_data.previous_version = component.previous_version_; + ping_data.next_version = component.next_version_; + ping_data.error_category = component.error_category_; + ping_data.error_code = component.error_code_; + ping_data.extra_code1 = component.extra_code1_; + ping_data.diff_error_category = component.diff_error_category_; + ping_data.diff_error_code = component.diff_error_code_; + ping_data.diff_update_failed = component.diff_update_failed(); + ping_data_.push_back(ping_data); + + events_ = component.GetEvents(); + + std::move(callback).Run(0, ""); +} + +const std::vector<MockPingManagerImpl::PingData>& +MockPingManagerImpl::ping_data() const { + return ping_data_; +} + +const std::vector<base::Value>& MockPingManagerImpl::events() const { + return events_; +} + +class UpdateClientTest : public testing::Test { + public: + UpdateClientTest(); + ~UpdateClientTest() override; + + protected: + void RunThreads(); + + // Returns the full path to a test file. + static base::FilePath TestFilePath(const char* file); + + scoped_refptr<update_client::TestConfigurator> config() { return config_; } + update_client::PersistedData* metadata() { return metadata_.get(); } + + base::OnceClosure quit_closure() { return runloop_.QuitClosure(); } + + private: + static constexpr int kNumWorkerThreads_ = 2; + + base::test::TaskEnvironment task_environment_; + base::RunLoop runloop_; + + scoped_refptr<update_client::TestConfigurator> config_ = + base::MakeRefCounted<TestConfigurator>(); + std::unique_ptr<TestingPrefServiceSimple> pref_ = + std::make_unique<TestingPrefServiceSimple>(); + std::unique_ptr<update_client::PersistedData> metadata_; + + DISALLOW_COPY_AND_ASSIGN(UpdateClientTest); +}; + +constexpr int UpdateClientTest::kNumWorkerThreads_; + +UpdateClientTest::UpdateClientTest() { + PersistedData::RegisterPrefs(pref_->registry()); + metadata_ = std::make_unique<PersistedData>(pref_.get(), nullptr); +} + +UpdateClientTest::~UpdateClientTest() { +} + +void UpdateClientTest::RunThreads() { + runloop_.Run(); + task_environment_.RunUntilIdle(); +} + +base::FilePath UpdateClientTest::TestFilePath(const char* file) { + base::FilePath path; + base::PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("components") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("update_client") + .AppendASCII(file); +} + +// Tests the scenario where one update check is done for one CRX. The CRX +// has no update. +TEST_F(UpdateClientTest, OneCrxNoUpdate) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx.version = base::Version("0.9"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + std::vector<base::Optional<CrxComponent>> component = {crx}; + return component; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(1u, ids_to_check.size()); + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check.front()); + EXPECT_EQ(1u, components.count(id)); + + auto& component = components.at(id); + + EXPECT_TRUE(component->is_foreground()); + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "noupdate"; + + ProtocolParser::Results results; + results.list.push_back(result); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), true, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the scenario where two CRXs are checked for updates. On CRX has +// an update, the other CRX does not. +TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx1; + crx1.name = "test_jebg"; + crx1.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx1.version = base::Version("0.9"); + crx1.installer = base::MakeRefCounted<TestInstaller>(); + crx1.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + + CrxComponent crx2; + crx2.name = "test_abag"; + crx2.pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash)); + crx2.version = base::Version("2.2"); + crx2.installer = base::MakeRefCounted<TestInstaller>(); + crx2.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + + return {crx1, crx2}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + /* + Mock the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='jebgalgnebhfojomionfpkfelancnnkf'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='jebgalgnebhfojomionfpkfelancnnkf.crx' + hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd + 7c9b12cb7cc067667bde87'/> + </packages> + </manifest> + </updatecheck> + </app> + <app appid='abagagagagagagagagagagagagagagag'> + <updatecheck status='noupdate'/> + </app> + </response> + */ + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(2u, ids_to_check.size()); + + ProtocolParser::Results results; + { + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; + package.hash_sha256 = + "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87"; + + ProtocolParser::Result result; + result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf"; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + + EXPECT_FALSE(components.at(id)->is_foreground()); + } + + { + const std::string id = "abagagagagagagagagagagagagagagag"; + EXPECT_EQ(id, ids_to_check[1]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "noupdate"; + results.list.push_back(result); + + EXPECT_FALSE(components.at(id)->is_foreground()); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 1843; + download_metrics.total_bytes = 1843; + download_metrics.download_time_ms = 1000; + + FilePath path; + EXPECT_TRUE(MakeTestFile( + TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); + + Result result; + result.error = 0; + result.response = path; + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this))); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(1u, ping_data.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id); + EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category)); + EXPECT_EQ(0, ping_data[0].error_code); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + } + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "abagagagagagagagagagagagagagagag")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "abagagagagagagagagagagagagagagag")).Times(1); + } + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf", + "abagagagagagagagagagagagagagagag"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the scenario where two CRXs are checked for updates. One CRX has +// an update but the server ignores the second CRX and returns no response for +// it. The second component gets an |UPDATE_RESPONSE_NOT_FOUND| error and +// transitions to the error state. +TEST_F(UpdateClientTest, TwoCrxUpdateFirstServerIgnoresSecond) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx1; + crx1.name = "test_jebg"; + crx1.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx1.version = base::Version("0.9"); + crx1.installer = base::MakeRefCounted<TestInstaller>(); + crx1.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + + CrxComponent crx2; + crx2.name = "test_abag"; + crx2.pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash)); + crx2.version = base::Version("2.2"); + crx2.installer = base::MakeRefCounted<TestInstaller>(); + crx2.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + + return {crx1, crx2}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + /* + Mock the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='jebgalgnebhfojomionfpkfelancnnkf'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='jebgalgnebhfojomionfpkfelancnnkf.crx' + hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd + 7c9b12cb7cc067667bde87'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(2u, ids_to_check.size()); + + ProtocolParser::Results results; + { + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; + package.hash_sha256 = + "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87"; + + ProtocolParser::Result result; + result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf"; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + + EXPECT_FALSE(components.at(id)->is_foreground()); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 1843; + download_metrics.total_bytes = 1843; + download_metrics.download_time_ms = 1000; + + FilePath path; + EXPECT_TRUE(MakeTestFile( + TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); + + Result result; + result.error = 0; + result.response = path; + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this))); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(1u, ping_data.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id); + EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category)); + EXPECT_EQ(0, ping_data[0].error_code); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + } + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "abagagagagagagagagagagagagagagag")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "abagagagagagagagagagagagagagagag")) + .Times(1) + .WillOnce(Invoke([&update_client](Events event, const std::string& id) { + CrxUpdateItem item; + EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item)); + EXPECT_EQ(ComponentState::kUpdateError, item.state); + EXPECT_EQ(5, static_cast<int>(item.error_category)); + EXPECT_EQ(-10004, item.error_code); + EXPECT_EQ(0, item.extra_code1); + })); + } + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf", + "abagagagagagagagagagagagagagagag"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the update check for two CRXs scenario when the second CRX does not +// provide a CrxComponent instance. In this case, the update is handled as +// if only one component were provided as an argument to the |Update| call +// with the exception that the second component still fires an event such as +// |COMPONENT_UPDATE_ERROR|. +TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentData) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx.version = base::Version("0.9"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + return {crx, base::nullopt}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + /* + Mock the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='jebgalgnebhfojomionfpkfelancnnkf'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='jebgalgnebhfojomionfpkfelancnnkf.crx' + hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd + 7c9b12cb7cc067667bde87'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(1u, ids_to_check.size()); + + ProtocolParser::Results results; + { + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; + package.hash_sha256 = + "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + + EXPECT_FALSE(components.at(id)->is_foreground()); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + FilePath path; + Result result; + if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 1843; + download_metrics.total_bytes = 1843; + download_metrics.download_time_ms = 1000; + + EXPECT_TRUE(MakeTestFile( + TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); + + result.error = 0; + result.response = path; + } else { + NOTREACHED(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this))); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(1u, ping_data.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id); + EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category)); + EXPECT_EQ(0, ping_data[0].error_code); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + } + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(1); + } + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf", + "ihfokbkgjpifnbbojhneepfflplebdkc"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the update check for two CRXs scenario when no CrxComponent data is +// provided for either component. In this case, no update check occurs, and +// |COMPONENT_UPDATE_ERROR| event fires for both components. +TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentDataAtAll) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + return {base::nullopt, base::nullopt}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + NOTREACHED(); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { NOTREACHED(); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + EXPECT_EQ(0u, MockPingManagerImpl::ping_data().size()); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(1); + } + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf", + "ihfokbkgjpifnbbojhneepfflplebdkc"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the scenario where there is a download timeout for the first +// CRX. The update for the first CRX fails. The update client waits before +// attempting the update for the second CRX. This update succeeds. +TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx1; + crx1.name = "test_jebg"; + crx1.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx1.version = base::Version("0.9"); + crx1.installer = base::MakeRefCounted<TestInstaller>(); + crx1.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + + CrxComponent crx2; + crx2.name = "test_ihfo"; + crx2.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash)); + crx2.version = base::Version("0.8"); + crx2.installer = base::MakeRefCounted<TestInstaller>(); + crx2.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + + return {crx1, crx2}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + /* + Mock the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='jebgalgnebhfojomionfpkfelancnnkf'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='jebgalgnebhfojomionfpkfelancnnkf.crx' + hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd + 7c9b12cb7cc067667bde87'/> + </packages> + </manifest> + </updatecheck> + </app> + <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx' + hash_sha256='813c59747e139a608b3b5fc49633affc6db574373f + 309f156ea6d27229c0b3f9'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(2u, ids_to_check.size()); + + ProtocolParser::Results results; + { + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; + package.hash_sha256 = + "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + } + + { + const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc"; + EXPECT_EQ(id, ids_to_check[1]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"; + package.hash_sha256 = + "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + FilePath path; + Result result; + if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = -118; + download_metrics.downloaded_bytes = 0; + download_metrics.total_bytes = 0; + download_metrics.download_time_ms = 1000; + + // The result must not include a file path in the case of errors. + result.error = -118; + } else if (url.path() == + "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 53638; + download_metrics.total_bytes = 53638; + download_metrics.download_time_ms = 2000; + + EXPECT_TRUE(MakeTestFile( + TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path)); + + result.error = 0; + result.response = path; + } else { + NOTREACHED(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this))); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(2u, ping_data.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id); + EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(1, static_cast<int>(ping_data[0].error_category)); + EXPECT_EQ(-118, ping_data[0].error_code); + EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[1].id); + EXPECT_EQ(base::Version("0.8"), ping_data[1].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[1].next_version); + EXPECT_EQ(0, static_cast<int>(ping_data[1].error_category)); + EXPECT_EQ(0, ping_data[1].error_code); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1) + .WillOnce(Invoke([&update_client](Events event, const std::string& id) { + CrxUpdateItem item; + EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item)); + EXPECT_EQ(ComponentState::kUpdateError, item.state); + EXPECT_EQ(1, static_cast<int>(item.error_category)); + EXPECT_EQ(-118, item.error_code); + EXPECT_EQ(0, item.extra_code1); + })); + } + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_WAIT, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + } + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf", + "ihfokbkgjpifnbbojhneepfflplebdkc"}; + + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the differential update scenario for one CRX. +TEST_F(UpdateClientTest, OneCrxDiffUpdate) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + static int num_calls = 0; + + // Must use the same stateful installer object. + static scoped_refptr<CrxInstaller> installer = + base::MakeRefCounted<VersionedTestInstaller>(); + + ++num_calls; + + CrxComponent crx; + crx.name = "test_ihfo"; + crx.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash)); + crx.installer = installer; + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + if (num_calls == 1) { + crx.version = base::Version("0.8"); + } else if (num_calls == 2) { + crx.version = base::Version("1.0"); + } else { + NOTREACHED(); + } + + return {crx}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + EXPECT_FALSE(session_id.empty()); + + static int num_call = 0; + ++num_call; + + ProtocolParser::Results results; + + if (num_call == 1) { + /* + Mock the following response: + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx' + hash_sha256='813c59747e139a608b3b5fc49633affc6db57437 + 3f309f156ea6d27229c0b3f9'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"; + package.hash_sha256 = + "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + } else if (num_call == 2) { + /* + Mock the following response: + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + <url codebasediff='http://localhost/download/'/> + </urls> + <manifest version='2.0' prodversionmin='11.0.1.0'> + <packages> + <package name='ihfokbkgjpifnbbojhneepfflplebdkc_2.crx' + namediff='ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx' + hash_sha256='1af337fbd19c72db0f870753bcd7711c3ae9dcaa + 0ecde26c262bad942b112990' + fp='22' + hashdiff_sha256='73c6e2d4f783fc4ca5481e89e0b8bfce7aec + 8ead3686290c94792658ec06f2f2'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"; + package.namediff = "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"; + package.hash_sha256 = + "1af337fbd19c72db0f870753bcd7711c3ae9dcaa0ecde26c262bad942b112990"; + package.hashdiff_sha256 = + "73c6e2d4f783fc4ca5481e89e0b8bfce7aec8ead3686290c94792658ec06f2f2"; + package.fingerprint = "22"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.crx_diffurls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "2.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + } else { + NOTREACHED(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + FilePath path; + Result result; + if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 53638; + download_metrics.total_bytes = 53638; + download_metrics.download_time_ms = 2000; + + EXPECT_TRUE(MakeTestFile( + TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path)); + + result.error = 0; + result.response = path; + } else if (url.path() == + "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 2105; + download_metrics.total_bytes = 2105; + download_metrics.download_time_ms = 1000; + + EXPECT_TRUE(MakeTestFile( + TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"), &path)); + + result.error = 0; + result.response = path; + } else { + NOTREACHED(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this))); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(2u, ping_data.size()); + EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[0].id); + EXPECT_EQ(base::Version("0.8"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category)); + EXPECT_EQ(0, ping_data[0].error_code); + EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[1].id); + EXPECT_EQ(base::Version("1.0"), ping_data[1].previous_version); + EXPECT_EQ(base::Version("2.0"), ping_data[1].next_version); + EXPECT_FALSE(ping_data[1].diff_update_failed); + EXPECT_EQ(0, static_cast<int>(ping_data[1].diff_error_category)); + EXPECT_EQ(0, ping_data[1].diff_error_code); + EXPECT_EQ(0, static_cast<int>(ping_data[1].error_category)); + EXPECT_EQ(0, ping_data[1].error_code); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + } + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"ihfokbkgjpifnbbojhneepfflplebdkc"}; + { + base::RunLoop runloop; + update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), + false, + base::BindOnce(&CompletionCallbackMock::Callback, + runloop.QuitClosure())); + runloop.Run(); + } + + { + base::RunLoop runloop; + update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), + false, + base::BindOnce(&CompletionCallbackMock::Callback, + runloop.QuitClosure())); + runloop.Run(); + } + + update_client->RemoveObserver(&observer); +} + +// Tests the update scenario for one CRX where the CRX installer returns +// an error. Tests that the |unpack_path| argument refers to a valid path +// then |Install| is called, then tests that the |unpack| path is deleted +// by the |update_client| code before the test ends. +TEST_F(UpdateClientTest, OneCrxInstallError) { + class MockInstaller : public CrxInstaller { + public: + MOCK_METHOD1(OnUpdateError, void(int error)); + MOCK_METHOD2(DoInstall, + void(const base::FilePath& unpack_path, + const Callback& callback)); + MOCK_METHOD2(GetInstalledFile, + bool(const std::string& file, base::FilePath* installed_file)); + MOCK_METHOD0(Uninstall, bool()); + + void Install(const base::FilePath& unpack_path, + const std::string& public_key, + Callback callback) override { + DoInstall(unpack_path, std::move(callback)); + + unpack_path_ = unpack_path; + EXPECT_TRUE(base::DirectoryExists(unpack_path_)); + base::PostTask( + FROM_HERE, {base::ThreadPool(), base::MayBlock()}, + base::BindOnce(std::move(callback), + CrxInstaller::Result(InstallError::GENERIC_ERROR))); + } + + protected: + ~MockInstaller() override { + // The unpack path is deleted unconditionally by the component state code, + // which is driving this installer. Therefore, the unpack path must not + // exist when this object is destroyed. + if (!unpack_path_.empty()) + EXPECT_FALSE(base::DirectoryExists(unpack_path_)); + } + + private: + // Contains the |unpack_path| argument of the Install call. + base::FilePath unpack_path_; + }; + + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + scoped_refptr<MockInstaller> installer = + base::MakeRefCounted<MockInstaller>(); + + EXPECT_CALL(*installer, OnUpdateError(_)).Times(0); + EXPECT_CALL(*installer, DoInstall(_, _)).Times(1); + EXPECT_CALL(*installer, GetInstalledFile(_, _)).Times(0); + EXPECT_CALL(*installer, Uninstall()).Times(0); + + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx.version = base::Version("0.9"); + crx.installer = installer; + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + + return {crx}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + /* + Mock the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='jebgalgnebhfojomionfpkfelancnnkf'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='jebgalgnebhfojomionfpkfelancnnkf.crx' + hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd + 7c9b12cb7cc067667bde87'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + EXPECT_FALSE(session_id.empty()); + + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; + package.hash_sha256 = + "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + + ProtocolParser::Results results; + results.list.push_back(result); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 1843; + download_metrics.total_bytes = 1843; + download_metrics.download_time_ms = 1000; + + FilePath path; + EXPECT_TRUE(MakeTestFile( + TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); + + Result result; + result.error = 0; + result.response = path; + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this))); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(1u, ping_data.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id); + EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(3, static_cast<int>(ping_data[0].error_category)); // kInstall. + EXPECT_EQ(9, ping_data[0].error_code); // kInstallerError. + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + } + + update_client->AddObserver(&observer); + + std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the fallback from differential to full update scenario for one CRX. +TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + static int num_calls = 0; + + // Must use the same stateful installer object. + static scoped_refptr<CrxInstaller> installer = + base::MakeRefCounted<VersionedTestInstaller>(); + + ++num_calls; + + CrxComponent crx; + crx.name = "test_ihfo"; + crx.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash)); + crx.installer = installer; + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + if (num_calls == 1) { + crx.version = base::Version("0.8"); + } else if (num_calls == 2) { + crx.version = base::Version("1.0"); + } else { + NOTREACHED(); + } + + return {crx}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + EXPECT_FALSE(session_id.empty()); + + static int num_call = 0; + ++num_call; + + ProtocolParser::Results results; + + if (num_call == 1) { + /* + Mock the following response: + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx' + hash_sha256='813c59747e139a608b3b5fc49633affc6db57437 + 3f309f156ea6d27229c0b3f9' + fp='1'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"; + package.hash_sha256 = + "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9"; + package.fingerprint = "1"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + } else if (num_call == 2) { + /* + Mock the following response: + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + <url codebasediff='http://localhost/download/'/> + </urls> + <manifest version='2.0' prodversionmin='11.0.1.0'> + <packages> + <package name='ihfokbkgjpifnbbojhneepfflplebdkc_2.crx' + namediff='ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx' + hash_sha256='1af337fbd19c72db0f870753bcd7711c3ae9dcaa + 0ecde26c262bad942b112990' + fp='22' + hashdiff_sha256='73c6e2d4f783fc4ca5481e89e0b8bfce7aec + 8ead3686290c94792658ec06f2f2'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"; + package.namediff = "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"; + package.hash_sha256 = + "1af337fbd19c72db0f870753bcd7711c3ae9dcaa0ecde26c262bad942b112990"; + package.hashdiff_sha256 = + "73c6e2d4f783fc4ca5481e89e0b8bfce7aec8ead3686290c94792658ec06f2f2"; + package.fingerprint = "22"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.crx_diffurls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "2.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + } else { + NOTREACHED(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + FilePath path; + Result result; + if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 53638; + download_metrics.total_bytes = 53638; + download_metrics.download_time_ms = 2000; + + EXPECT_TRUE(MakeTestFile( + TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path)); + + result.error = 0; + result.response = path; + } else if (url.path() == + "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx") { + // A download error is injected on this execution path. + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = -1; + download_metrics.downloaded_bytes = 0; + download_metrics.total_bytes = 2105; + download_metrics.download_time_ms = 1000; + + // The response must not include a file path in the case of errors. + result.error = -1; + } else if (url.path() == + "/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 53855; + download_metrics.total_bytes = 53855; + download_metrics.download_time_ms = 1000; + + EXPECT_TRUE(MakeTestFile( + TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"), &path)); + + result.error = 0; + result.response = path; + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this))); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(2u, ping_data.size()); + EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[0].id); + EXPECT_EQ(base::Version("0.8"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category)); + EXPECT_EQ(0, ping_data[0].error_code); + EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[1].id); + EXPECT_EQ(base::Version("1.0"), ping_data[1].previous_version); + EXPECT_EQ(base::Version("2.0"), ping_data[1].next_version); + EXPECT_EQ(0, static_cast<int>(ping_data[1].error_category)); + EXPECT_EQ(0, ping_data[1].error_code); + EXPECT_TRUE(ping_data[1].diff_update_failed); + EXPECT_EQ(1, static_cast<int>(ping_data[1].diff_error_category)); + EXPECT_EQ(-1, ping_data[1].diff_error_code); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); + } + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"ihfokbkgjpifnbbojhneepfflplebdkc"}; + + { + base::RunLoop runloop; + update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), + false, + base::BindOnce(&CompletionCallbackMock::Callback, + runloop.QuitClosure())); + runloop.Run(); + } + + { + base::RunLoop runloop; + update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), + false, + base::BindOnce(&CompletionCallbackMock::Callback, + runloop.QuitClosure())); + runloop.Run(); + } + + update_client->RemoveObserver(&observer); +} + +// Tests the queuing of update checks. In this scenario, two update checks are +// done for one CRX. The second update check call is queued up and will run +// after the first check has completed. The CRX has no updates. +TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx.version = base::Version("0.9"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + return {crx}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + static int num_call = 0; + ++num_call; + + EXPECT_EQ(Error::NONE, error); + + if (num_call == 2) + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(1u, ids_to_check.size()); + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check.front()); + EXPECT_EQ(1u, components.count(id)); + + auto& component = components.at(id); + + EXPECT_FALSE(component->is_foreground()); + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "noupdate"; + ProtocolParser::Results results; + results.list.push_back(result); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the install of one CRX. +TEST_F(UpdateClientTest, OneCrxInstall) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx.version = base::Version("0.0"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + return {crx}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + /* + Mock the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='jebgalgnebhfojomionfpkfelancnnkf'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='jebgalgnebhfojomionfpkfelancnnkf.crx' + hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd + 7c9b12cb7cc067667bde87'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(1u, ids_to_check.size()); + + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; + package.hash_sha256 = + "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + + ProtocolParser::Results results; + results.list.push_back(result); + + // Verify that calling Install sets ondemand. + EXPECT_TRUE(components.at(id)->is_foreground()); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + FilePath path; + Result result; + if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 1843; + download_metrics.total_bytes = 1843; + download_metrics.download_time_ms = 1000; + + EXPECT_TRUE(MakeTestFile( + TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); + + result.error = 0; + result.response = path; + } else { + NOTREACHED(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this))); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(1u, ping_data.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id); + EXPECT_EQ(base::Version("0.0"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category)); + EXPECT_EQ(0, ping_data[0].error_code); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + + update_client->AddObserver(&observer); + + update_client->Install( + std::string("jebgalgnebhfojomionfpkfelancnnkf"), + base::BindOnce(&DataCallbackMock::Callback), + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the install of one CRX when no component data is provided. This +// results in an install error. +TEST_F(UpdateClientTest, OneCrxInstallNoCrxComponentData) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + return {base::nullopt}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + NOTREACHED(); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { NOTREACHED(); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + EXPECT_EQ(0u, MockPingManagerImpl::ping_data().size()); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1) + .WillOnce(Invoke([&update_client](Events event, const std::string& id) { + // Tests that the state of the component when the CrxComponent data + // is not provided. In this case, the optional |item.component| instance + // is not present. + CrxUpdateItem item; + EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item)); + EXPECT_EQ(ComponentState::kUpdateError, item.state); + EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", item.id.c_str()); + EXPECT_FALSE(item.component); + EXPECT_EQ(ErrorCategory::kService, item.error_category); + EXPECT_EQ(static_cast<int>(Error::CRX_NOT_FOUND), item.error_code); + EXPECT_EQ(0, item.extra_code1); + })); + + update_client->AddObserver(&observer); + + update_client->Install( + std::string("jebgalgnebhfojomionfpkfelancnnkf"), + base::BindOnce(&DataCallbackMock::Callback), + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests that overlapping installs of the same CRX result in an error. +TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx.version = base::Version("0.0"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + return {crx}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + static int num_call = 0; + ++num_call; + + EXPECT_LE(num_call, 2); + + if (num_call == 1) { + EXPECT_EQ(Error::UPDATE_IN_PROGRESS, error); + return; + } + if (num_call == 2) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(1u, ids_to_check.size()); + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check.front()); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "noupdate"; + + ProtocolParser::Results results; + results.list.push_back(result); + + // Verify that calling Install sets |is_foreground| for the component. + EXPECT_TRUE(components.at(id)->is_foreground()); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + + update_client->AddObserver(&observer); + + update_client->Install( + std::string("jebgalgnebhfojomionfpkfelancnnkf"), + base::BindOnce(&DataCallbackMock::Callback), + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + update_client->Install( + std::string("jebgalgnebhfojomionfpkfelancnnkf"), + base::BindOnce(&DataCallbackMock::Callback), + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests that UpdateClient::Update returns Error::INVALID_ARGUMENT when +// the |ids| parameter is empty. +TEST_F(UpdateClientTest, EmptyIdList) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + return {}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + DCHECK_EQ(Error::INVALID_ARGUMENT, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + NOTREACHED(); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + const std::vector<std::string> empty_id_list; + update_client->Update( + empty_id_list, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + RunThreads(); +} + +TEST_F(UpdateClientTest, SendUninstallPing) { + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return nullptr; + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + NOTREACHED(); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return nullptr; + } + + private: + MockCrxDownloader() : CrxDownloader(nullptr) {} + ~MockCrxDownloader() override {} + + void DoStartDownload(const GURL& url) override {} + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(1u, ping_data.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id); + EXPECT_EQ(base::Version("1.2.3.4"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("0"), ping_data[0].next_version); + EXPECT_EQ(10, ping_data[0].extra_code1); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + update_client->SendUninstallPing( + "jebgalgnebhfojomionfpkfelancnnkf", base::Version("1.2.3.4"), 10, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); +} + +TEST_F(UpdateClientTest, RetryAfter) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx.version = base::Version("0.9"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + return {crx}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + static int num_call = 0; + ++num_call; + + EXPECT_LE(num_call, 4); + + if (num_call == 1) { + EXPECT_EQ(Error::NONE, error); + } else if (num_call == 2) { + // This request is throttled since the update engine received a + // positive |retry_after_sec| value in the update check response. + EXPECT_EQ(Error::RETRY_LATER, error); + } else if (num_call == 3) { + // This request is a foreground Install, which is never throttled. + // The update engine received a |retry_after_sec| value of 0, which + // resets the throttling. + EXPECT_EQ(Error::NONE, error); + } else if (num_call == 4) { + // This request succeeds since there is no throttling in effect. + EXPECT_EQ(Error::NONE, error); + } + + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + EXPECT_FALSE(session_id.empty()); + + static int num_call = 0; + ++num_call; + + EXPECT_LE(num_call, 3); + + int retry_after_sec(0); + if (num_call == 1) { + // Throttle the next call. + retry_after_sec = 60 * 60; // 1 hour. + } + + EXPECT_EQ(1u, ids_to_check.size()); + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check.front()); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "noupdate"; + + ProtocolParser::Results results; + results.list.push_back(result); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, retry_after_sec)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"}; + { + // The engine handles this Update call but responds with a valid + // |retry_after_sec|, which causes subsequent calls to fail. + base::RunLoop runloop; + update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), + false, + base::BindOnce(&CompletionCallbackMock::Callback, + runloop.QuitClosure())); + runloop.Run(); + } + + { + // This call will result in a completion callback invoked with + // Error::ERROR_UPDATE_RETRY_LATER. + base::RunLoop runloop; + update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), + false, + base::BindOnce(&CompletionCallbackMock::Callback, + runloop.QuitClosure())); + runloop.Run(); + } + + { + // The Install call is handled, and the throttling is reset due to + // the value of |retry_after_sec| in the completion callback. + base::RunLoop runloop; + update_client->Install(std::string("jebgalgnebhfojomionfpkfelancnnkf"), + base::BindOnce(&DataCallbackMock::Callback), + base::BindOnce(&CompletionCallbackMock::Callback, + runloop.QuitClosure())); + runloop.Run(); + } + + { + // This call succeeds. + base::RunLoop runloop; + update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), + false, + base::BindOnce(&CompletionCallbackMock::Callback, + runloop.QuitClosure())); + runloop.Run(); + } + + update_client->RemoveObserver(&observer); +} + +// Tests the update check for two CRXs scenario. The first component supports +// the group policy to enable updates, and has its updates disabled. The second +// component has an update. The server does not honor the "updatedisabled" +// attribute and returns updates for both components. However, the update for +// the first component is not applied and the client responds with a +// (SERVICE_ERROR, UPDATE_DISABLED) +TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx1; + crx1.name = "test_jebg"; + crx1.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx1.version = base::Version("0.9"); + crx1.installer = base::MakeRefCounted<TestInstaller>(); + crx1.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + crx1.supports_group_policy_enable_component_updates = true; + + CrxComponent crx2; + crx2.name = "test_ihfo"; + crx2.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash)); + crx2.version = base::Version("0.8"); + crx2.installer = base::MakeRefCounted<TestInstaller>(); + crx2.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + + return {crx1, crx2}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + /* + Mock the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='jebgalgnebhfojomionfpkfelancnnkf'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='jebgalgnebhfojomionfpkfelancnnkf.crx' + hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd + 7c9b12cb7cc067667bde87'/> + </packages> + </manifest> + </updatecheck> + </app> + <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx' + hash_sha256='813c59747e139a608b3b5fc49633affc6db574373f + 309f156ea6d27229c0b3f9'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + + // UpdateClient reads the state of |enabled_component_updates| from the + // configurator instance, persists its value in the corresponding + // update context, and propagates it down to each of the update actions, + // and further down to the UpdateChecker instance. + EXPECT_FALSE(session_id.empty()); + EXPECT_FALSE(enabled_component_updates); + EXPECT_EQ(2u, ids_to_check.size()); + + ProtocolParser::Results results; + { + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; + package.hash_sha256 = + "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + } + + { + const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc"; + EXPECT_EQ(id, ids_to_check[1]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"; + package.hash_sha256 = + "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + results.list.push_back(result); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + FilePath path; + Result result; + if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 53638; + download_metrics.total_bytes = 53638; + download_metrics.download_time_ms = 2000; + + EXPECT_TRUE(MakeTestFile( + TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path)); + + result.error = 0; + result.response = path; + } else { + NOTREACHED(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this))); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + const auto ping_data = MockPingManagerImpl::ping_data(); + EXPECT_EQ(2u, ping_data.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id); + EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(4, static_cast<int>(ping_data[0].error_category)); + EXPECT_EQ(2, ping_data[0].error_code); + EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[1].id); + EXPECT_EQ(base::Version("0.8"), ping_data[1].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[1].next_version); + EXPECT_EQ(0, static_cast<int>(ping_data[1].error_category)); + EXPECT_EQ(0, ping_data[1].error_code); + } + }; + + // Disables updates for the components declaring support for the group policy. + config()->SetEnabledComponentUpdates(false); + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + } + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(AtLeast(1)); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(1); + } + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf", + "ihfokbkgjpifnbbojhneepfflplebdkc"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the scenario where the update check fails. +TEST_F(UpdateClientTest, OneCrxUpdateCheckFails) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx.version = base::Version("0.9"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + return {crx}; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::UPDATE_CHECK_ERROR, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(1u, ids_to_check.size()); + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_EQ(id, ids_to_check.front()); + EXPECT_EQ(1u, components.count(id)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(update_check_callback), base::nullopt, + ErrorCategory::kUpdateCheck, -1, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1) + .WillOnce(Invoke([&update_client](Events event, const std::string& id) { + CrxUpdateItem item; + EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item)); + EXPECT_EQ(ComponentState::kUpdateError, item.state); + EXPECT_EQ(5, static_cast<int>(item.error_category)); + EXPECT_EQ(-1, item.error_code); + EXPECT_EQ(0, item.extra_code1); + })); + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), false, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +// Tests the scenario where the server responds with different values for +// application status. +TEST_F(UpdateClientTest, OneCrxErrorUnknownApp) { + class DataCallbackMock { + public: + static std::vector<base::Optional<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::vector<base::Optional<CrxComponent>> component; + { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash)); + crx.version = base::Version("0.9"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + component.push_back(crx); + } + { + CrxComponent crx; + crx.name = "test_abag"; + crx.pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash)); + crx.version = base::Version("0.1"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + component.push_back(crx); + } + { + CrxComponent crx; + crx.name = "test_ihfo"; + crx.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash)); + crx.version = base::Version("0.2"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + component.push_back(crx); + } + { + CrxComponent crx; + crx.name = "test_gjpm"; + crx.pk_hash.assign(gjpm_hash, gjpm_hash + base::size(gjpm_hash)); + crx.version = base::Version("0.3"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + component.push_back(crx); + } + return component; + } + }; + + class CompletionCallbackMock { + public: + static void Callback(base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + } + }; + + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(4u, ids_to_check.size()); + + const std::string update_response = + ")]}'" + R"({"response": {)" + R"( "protocol": "3.1",)" + R"( "app": [)" + R"({"appid": "jebgalgnebhfojomionfpkfelancnnkf",)" + R"( "status": "error-unknownApplication"},)" + R"({"appid": "abagagagagagagagagagagagagagagag",)" + R"( "status": "restricted"},)" + R"({"appid": "ihfokbkgjpifnbbojhneepfflplebdkc",)" + R"( "status": "error-invalidAppId"},)" + R"({"appid": "gjpmebpgbhcamgdgjcmnjfhggjpgcimm",)" + R"( "status": "error-foobarApp"})" + R"(]}})"; + + const auto parser = ProtocolHandlerFactoryJSON().CreateParser(); + EXPECT_TRUE(parser->Parse(update_response)); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(update_check_callback), parser->results(), + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + MockObserver observer; + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "jebgalgnebhfojomionfpkfelancnnkf")) + .Times(1) + .WillOnce(Invoke([&update_client](Events event, const std::string& id) { + CrxUpdateItem item; + EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item)); + EXPECT_EQ(ComponentState::kUpdateError, item.state); + EXPECT_EQ(5, static_cast<int>(item.error_category)); + EXPECT_EQ(-10006, item.error_code); // UNKNOWN_APPPLICATION. + EXPECT_EQ(0, item.extra_code1); + })); + } + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "abagagagagagagagagagagagagagagag")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "abagagagagagagagagagagagagagagag")) + .Times(1) + .WillOnce(Invoke([&update_client](Events event, const std::string& id) { + CrxUpdateItem item; + EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item)); + EXPECT_EQ(ComponentState::kUpdateError, item.state); + EXPECT_EQ(5, static_cast<int>(item.error_category)); + EXPECT_EQ(-10007, item.error_code); // RESTRICTED_APPLICATION. + EXPECT_EQ(0, item.extra_code1); + })); + } + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "ihfokbkgjpifnbbojhneepfflplebdkc")) + .Times(1) + .WillOnce(Invoke([&update_client](Events event, const std::string& id) { + CrxUpdateItem item; + EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item)); + EXPECT_EQ(ComponentState::kUpdateError, item.state); + EXPECT_EQ(5, static_cast<int>(item.error_category)); + EXPECT_EQ(-10008, item.error_code); // INVALID_APPID. + EXPECT_EQ(0, item.extra_code1); + })); + } + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "gjpmebpgbhcamgdgjcmnjfhggjpgcimm")) + .Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR, + "gjpmebpgbhcamgdgjcmnjfhggjpgcimm")) + .Times(1) + .WillOnce(Invoke([&update_client](Events event, const std::string& id) { + CrxUpdateItem item; + EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item)); + EXPECT_EQ(ComponentState::kUpdateError, item.state); + EXPECT_EQ(5, static_cast<int>(item.error_category)); + EXPECT_EQ(-10004, item.error_code); // UPDATE_RESPONSE_NOT_FOUND. + EXPECT_EQ(0, item.extra_code1); + })); + } + + update_client->AddObserver(&observer); + + const std::vector<std::string> ids = { + "jebgalgnebhfojomionfpkfelancnnkf", "abagagagagagagagagagagagagagagag", + "ihfokbkgjpifnbbojhneepfflplebdkc", "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"}; + update_client->Update( + ids, base::BindOnce(&DataCallbackMock::Callback), true, + base::BindOnce(&CompletionCallbackMock::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); +} + +#if defined(OS_WIN) // ActionRun is only implemented on Windows. + +// Tests that a run action in invoked in the CRX install scenario. +TEST_F(UpdateClientTest, ActionRun_Install) { + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + /* + Mock the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='gjpmebpgbhcamgdgjcmnjfhggjpgcimm'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='runaction_test_win.crx3' + hash_sha256='89290a0d2ff21ca5b45e109c6cc859ab5fe294e19c102d54acd321429c372cea'/> + </packages> + </manifest> + <actions>" + <action run='ChromeRecovery.crx3'/>" + </actions>" + </updatecheck> + </app> + </response> + */ + EXPECT_FALSE(session_id.empty()); + EXPECT_TRUE(enabled_component_updates); + EXPECT_EQ(1u, ids_to_check.size()); + + const std::string id = "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result::Manifest::Package package; + package.name = "runaction_test_win.crx3"; + package.hash_sha256 = + "89290a0d2ff21ca5b45e109c6cc859ab5fe294e19c102d54acd321429c372cea"; + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "ok"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + result.action_run = "ChromeRecovery.crx3"; + + ProtocolParser::Results results; + results.list.push_back(result); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + FilePath path; + Result result; + if (url.path() == "/download/runaction_test_win.crx3") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 1843; + download_metrics.total_bytes = 1843; + download_metrics.download_time_ms = 1000; + + EXPECT_TRUE( + MakeTestFile(TestFilePath("runaction_test_win.crx3"), &path)); + + result.error = 0; + result.response = path; + } else { + NOTREACHED(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + EXPECT_EQ(3u, events().size()); + + /* + "<event eventtype="14" eventresult="1" downloader="unknown" " + "url="http://localhost/download/runaction_test_win.crx3" + "downloaded=1843 " + "total=1843 download_time_ms="1000" previousversion="0.0" " + "nextversion="1.0"/>" + */ + const auto& event0 = events()[0]; + EXPECT_EQ(14, event0.FindKey("eventtype")->GetInt()); + EXPECT_EQ(1, event0.FindKey("eventresult")->GetInt()); + EXPECT_EQ("unknown", event0.FindKey("downloader")->GetString()); + EXPECT_EQ("http://localhost/download/runaction_test_win.crx3", + event0.FindKey("url")->GetString()); + EXPECT_EQ(1843, event0.FindKey("downloaded")->GetDouble()); + EXPECT_EQ(1843, event0.FindKey("total")->GetDouble()); + EXPECT_EQ(1000, event0.FindKey("download_time_ms")->GetDouble()); + EXPECT_EQ("0.0", event0.FindKey("previousversion")->GetString()); + EXPECT_EQ("1.0", event0.FindKey("nextversion")->GetString()); + + // "<event eventtype="42" eventresult="1" errorcode="1877345072"/>" + const auto& event1 = events()[1]; + EXPECT_EQ(42, event1.FindKey("eventtype")->GetInt()); + EXPECT_EQ(1, event1.FindKey("eventresult")->GetInt()); + EXPECT_EQ(1877345072, event1.FindKey("errorcode")->GetInt()); + + // "<event eventtype=\"3\" eventresult=\"1\" previousversion=\"0.0\" " + // "nextversion=\"1.0\"/>", + const auto& event2 = events()[2]; + EXPECT_EQ(3, event2.FindKey("eventtype")->GetInt()); + EXPECT_EQ(1, event1.FindKey("eventresult")->GetInt()); + EXPECT_EQ("0.0", event0.FindKey("previousversion")->GetString()); + EXPECT_EQ("1.0", event0.FindKey("nextversion")->GetString()); + } + }; + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + // The action is a program which returns 1877345072 as a hardcoded value. + update_client->Install( + std::string("gjpmebpgbhcamgdgjcmnjfhggjpgcimm"), + base::BindOnce([](const std::vector<std::string>& ids) { + CrxComponent crx; + crx.name = "test_niea"; + crx.pk_hash.assign(gjpm_hash, gjpm_hash + base::size(gjpm_hash)); + crx.version = base::Version("0.0"); + crx.installer = base::MakeRefCounted<VersionedTestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + return std::vector<base::Optional<CrxComponent>>{crx}; + }), + base::BindOnce( + [](base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + }, + quit_closure())); + + RunThreads(); +} + +// Tests that a run action is invoked in an update scenario when there was +// no update. +TEST_F(UpdateClientTest, ActionRun_NoUpdate) { + class MockUpdateChecker : public UpdateChecker { + public: + static std::unique_ptr<UpdateChecker> Create( + scoped_refptr<Configurator> config, + PersistedData* metadata) { + return std::make_unique<MockUpdateChecker>(); + } + + void CheckForUpdates( + const std::string& session_id, + const std::vector<std::string>& ids_to_check, + const IdToComponentPtrMap& components, + const base::flat_map<std::string, std::string>& additional_attributes, + bool enabled_component_updates, + UpdateCheckCallback update_check_callback) override { + /* + Mock the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.1'> + <app appid='gjpmebpgbhcamgdgjcmnjfhggjpgcimm'> + <updatecheck status='noupdate'> + <actions>" + <action run=ChromeRecovery.crx3'/>" + </actions>" + </updatecheck> + </app> + </response> + */ + EXPECT_FALSE(session_id.empty()); + EXPECT_EQ(1u, ids_to_check.size()); + const std::string id = "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"; + EXPECT_EQ(id, ids_to_check[0]); + EXPECT_EQ(1u, components.count(id)); + + ProtocolParser::Result result; + result.extension_id = id; + result.status = "noupdate"; + result.action_run = "ChromeRecovery.crx3"; + + ProtocolParser::Results results; + results.list.push_back(result); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_check_callback), results, + ErrorCategory::kNone, 0, 0)); + } + }; + + class MockCrxDownloader : public CrxDownloader { + public: + static std::unique_ptr<CrxDownloader> Create( + bool is_background_download, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) { + return std::make_unique<MockCrxDownloader>(); + } + + MockCrxDownloader() : CrxDownloader(nullptr) {} + + private: + void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + EXPECT_EQ(1u, events().size()); + + // "<event eventtype="42" eventresult="1" errorcode="1877345072"/>" + const auto& event = events()[0]; + EXPECT_EQ(42, event.FindKey("eventtype")->GetInt()); + EXPECT_EQ(1, event.FindKey("eventresult")->GetInt()); + EXPECT_EQ(1877345072, event.FindKey("errorcode")->GetInt()); + } + }; + + // Unpack the CRX to mock an existing install to be updated. The payload to + // run is going to be picked up from this directory. + base::FilePath unpack_path; + { + base::RunLoop runloop; + base::OnceClosure quit_closure = runloop.QuitClosure(); + + auto config = base::MakeRefCounted<TestConfigurator>(); + auto component_unpacker = base::MakeRefCounted<ComponentUnpacker>( + std::vector<uint8_t>(std::begin(gjpm_hash), std::end(gjpm_hash)), + TestFilePath("runaction_test_win.crx3"), nullptr, + config->GetUnzipperFactory()->Create(), + config->GetPatcherFactory()->Create(), + crx_file::VerifierFormat::CRX2_OR_CRX3); + + component_unpacker->Unpack(base::BindOnce( + [](base::FilePath* unpack_path, base::OnceClosure quit_closure, + const ComponentUnpacker::Result& result) { + EXPECT_EQ(UnpackerError::kNone, result.error); + EXPECT_EQ(0, result.extended_error); + *unpack_path = result.unpack_path; + std::move(quit_closure).Run(); + }, + &unpack_path, runloop.QuitClosure())); + + runloop.Run(); + } + + EXPECT_FALSE(unpack_path.empty()); + EXPECT_TRUE(base::DirectoryExists(unpack_path)); + int64_t file_size = 0; + EXPECT_TRUE(base::GetFileSize(unpack_path.AppendASCII("ChromeRecovery.crx3"), + &file_size)); + EXPECT_EQ(44582, file_size); + + base::ScopedTempDir unpack_path_owner; + EXPECT_TRUE(unpack_path_owner.Set(unpack_path)); + + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + &MockUpdateChecker::Create, &MockCrxDownloader::Create); + + // The action is a program which returns 1877345072 as a hardcoded value. + const std::vector<std::string> ids = {"gjpmebpgbhcamgdgjcmnjfhggjpgcimm"}; + update_client->Update( + ids, + base::BindOnce( + [](const base::FilePath& unpack_path, + const std::vector<std::string>& ids) { + CrxComponent crx; + crx.name = "test_niea"; + crx.pk_hash.assign(gjpm_hash, gjpm_hash + base::size(gjpm_hash)); + crx.version = base::Version("1.0"); + crx.installer = + base::MakeRefCounted<ReadOnlyTestInstaller>(unpack_path); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3; + return std::vector<base::Optional<CrxComponent>>{crx}; + }, + unpack_path), + false, + base::BindOnce( + [](base::OnceClosure quit_closure, Error error) { + EXPECT_EQ(Error::NONE, error); + std::move(quit_closure).Run(); + }, + quit_closure())); + + RunThreads(); +} + +#endif // OS_WIN + +} // namespace update_client
diff --git a/src/updater_components/update_client/update_engine.cc b/src/updater_components/update_client/update_engine.cc new file mode 100644 index 0000000..a6d6920 --- /dev/null +++ b/src/updater_components/update_client/update_engine.cc
@@ -0,0 +1,438 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/update_engine.h" + +#include <algorithm> +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/guid.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/optional.h" +#include "base/stl_util.h" +#include "base/strings/strcat.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/prefs/pref_service.h" +#include "components/update_client/component.h" +#include "components/update_client/configurator.h" +#include "components/update_client/crx_update_item.h" +#include "components/update_client/persisted_data.h" +#include "components/update_client/protocol_parser.h" +#include "components/update_client/update_checker.h" +#include "components/update_client/update_client_errors.h" +#include "components/update_client/utils.h" + +namespace update_client { + +UpdateContext::UpdateContext( + scoped_refptr<Configurator> config, + bool is_foreground, + const std::vector<std::string>& ids, + UpdateClient::CrxDataCallback crx_data_callback, + const UpdateEngine::NotifyObserversCallback& notify_observers_callback, + UpdateEngine::Callback callback, + CrxDownloader::Factory crx_downloader_factory) + : config(config), + is_foreground(is_foreground), + enabled_component_updates(config->EnabledComponentUpdates()), + ids(ids), + crx_data_callback(std::move(crx_data_callback)), + notify_observers_callback(notify_observers_callback), + callback(std::move(callback)), + crx_downloader_factory(crx_downloader_factory), + session_id(base::StrCat({"{", base::GenerateGUID(), "}"})) { + for (const auto& id : ids) { + components.insert( + std::make_pair(id, std::make_unique<Component>(*this, id))); + } +} + +UpdateContext::~UpdateContext() {} + +UpdateEngine::UpdateEngine( + scoped_refptr<Configurator> config, + UpdateChecker::Factory update_checker_factory, + CrxDownloader::Factory crx_downloader_factory, + scoped_refptr<PingManager> ping_manager, + const NotifyObserversCallback& notify_observers_callback) + : config_(config), + update_checker_factory_(update_checker_factory), + crx_downloader_factory_(crx_downloader_factory), + ping_manager_(ping_manager), + metadata_( + std::make_unique<PersistedData>(config->GetPrefService(), + config->GetActivityDataService())), + notify_observers_callback_(notify_observers_callback) {} + +UpdateEngine::~UpdateEngine() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void UpdateEngine::Update(bool is_foreground, + const std::vector<std::string>& ids, + UpdateClient::CrxDataCallback crx_data_callback, + Callback callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (ids.empty()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(callback), Error::INVALID_ARGUMENT)); + return; + } + + if (IsThrottled(is_foreground)) { + // TODO(xiaochu): remove this log after https://crbug.com/851151 is fixed. + VLOG(1) << "Background update is throttled for following components:"; + for (const auto& id : ids) { + VLOG(1) << "id:" << id; + } + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), Error::RETRY_LATER)); + return; + } + + const auto update_context = base::MakeRefCounted<UpdateContext>( + config_, is_foreground, ids, std::move(crx_data_callback), + notify_observers_callback_, std::move(callback), crx_downloader_factory_); + DCHECK(!update_context->session_id.empty()); + + const auto result = update_contexts_.insert( + std::make_pair(update_context->session_id, update_context)); + DCHECK(result.second); + + // Calls out to get the corresponding CrxComponent data for the CRXs in this + // update context. + const auto crx_components = + std::move(update_context->crx_data_callback).Run(update_context->ids); + DCHECK_EQ(update_context->ids.size(), crx_components.size()); + + for (size_t i = 0; i != update_context->ids.size(); ++i) { + const auto& id = update_context->ids[i]; + + DCHECK(update_context->components[id]->state() == ComponentState::kNew); + + const auto crx_component = crx_components[i]; + if (crx_component) { + // This component can be checked for updates. + DCHECK_EQ(id, GetCrxComponentID(*crx_component)); + auto& component = update_context->components[id]; + component->set_crx_component(*crx_component); + component->set_previous_version(component->crx_component()->version); + component->set_previous_fp(component->crx_component()->fingerprint); + update_context->components_to_check_for_updates.push_back(id); + } else { + // |CrxDataCallback| did not return a CrxComponent instance for this + // component, which most likely, has been uninstalled. This component + // is going to be transitioned to an error state when the its |Handle| + // method is called later on by the engine. + update_context->component_queue.push(id); + } + } + + if (update_context->components_to_check_for_updates.empty()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent, + base::Unretained(this), update_context)); + return; + } + + for (const auto& id : update_context->components_to_check_for_updates) + update_context->components[id]->Handle( + base::BindOnce(&UpdateEngine::ComponentCheckingForUpdatesStart, + base::Unretained(this), update_context, id)); +} + +void UpdateEngine::ComponentCheckingForUpdatesStart( + scoped_refptr<UpdateContext> update_context, + const std::string& id) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(update_context); + + DCHECK_EQ(1u, update_context->components.count(id)); + DCHECK(update_context->components.at(id)); + + // Handle |kChecking| state. + auto& component = *update_context->components.at(id); + component.Handle( + base::BindOnce(&UpdateEngine::ComponentCheckingForUpdatesComplete, + base::Unretained(this), update_context)); + + ++update_context->num_components_ready_to_check; + if (update_context->num_components_ready_to_check < + update_context->components_to_check_for_updates.size()) { + return; + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&UpdateEngine::DoUpdateCheck, + base::Unretained(this), update_context)); +} + +void UpdateEngine::DoUpdateCheck(scoped_refptr<UpdateContext> update_context) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(update_context); + + update_context->update_checker = + update_checker_factory_(config_, metadata_.get()); + + update_context->update_checker->CheckForUpdates( + update_context->session_id, + update_context->components_to_check_for_updates, + update_context->components, config_->ExtraRequestParams(), + update_context->enabled_component_updates, + base::BindOnce(&UpdateEngine::UpdateCheckResultsAvailable, + base::Unretained(this), update_context)); +} + +void UpdateEngine::UpdateCheckResultsAvailable( + scoped_refptr<UpdateContext> update_context, + const base::Optional<ProtocolParser::Results>& results, + ErrorCategory error_category, + int error, + int retry_after_sec) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(update_context); + + update_context->retry_after_sec = retry_after_sec; + + // Only positive values for throttle_sec are effective. 0 means that no + // throttling occurs and it resets |throttle_updates_until_|. + // Negative values are not trusted and are ignored. + constexpr int kMaxRetryAfterSec = 24 * 60 * 60; // 24 hours. + const int throttle_sec = + std::min(update_context->retry_after_sec, kMaxRetryAfterSec); + if (throttle_sec >= 0) { + throttle_updates_until_ = + throttle_sec ? base::TimeTicks::Now() + + base::TimeDelta::FromSeconds(throttle_sec) + : base::TimeTicks(); + } + + update_context->update_check_error = error; + + if (error) { + DCHECK(!results); + for (const auto& id : update_context->components_to_check_for_updates) { + DCHECK_EQ(1u, update_context->components.count(id)); + auto& component = update_context->components.at(id); + component->SetUpdateCheckResult(base::nullopt, + ErrorCategory::kUpdateCheck, error); + } + return; + } + + DCHECK(results); + DCHECK_EQ(0, error); + + std::map<std::string, ProtocolParser::Result> id_to_result; + for (const auto& result : results->list) + id_to_result[result.extension_id] = result; + + for (const auto& id : update_context->components_to_check_for_updates) { + DCHECK_EQ(1u, update_context->components.count(id)); + auto& component = update_context->components.at(id); + const auto& it = id_to_result.find(id); + if (it != id_to_result.end()) { + const auto result = it->second; + const auto error = [](const std::string& status) { + // First, handle app status literals which can be folded down as an + // updatecheck status + if (status == "error-unknownApplication") + return std::make_pair(ErrorCategory::kUpdateCheck, + ProtocolError::UNKNOWN_APPLICATION); + if (status == "restricted") + return std::make_pair(ErrorCategory::kUpdateCheck, + ProtocolError::RESTRICTED_APPLICATION); + if (status == "error-invalidAppId") + return std::make_pair(ErrorCategory::kUpdateCheck, + ProtocolError::INVALID_APPID); + // If the parser has return a valid result and the status is not one of + // the literals above, then this must be a success an not a parse error. + return std::make_pair(ErrorCategory::kNone, ProtocolError::NONE); + }(result.status); + component->SetUpdateCheckResult(result, error.first, + static_cast<int>(error.second)); + } else { + component->SetUpdateCheckResult( + base::nullopt, ErrorCategory::kUpdateCheck, + static_cast<int>(ProtocolError::UPDATE_RESPONSE_NOT_FOUND)); + } + } +} + +void UpdateEngine::ComponentCheckingForUpdatesComplete( + scoped_refptr<UpdateContext> update_context) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(update_context); + + ++update_context->num_components_checked; + if (update_context->num_components_checked < + update_context->components_to_check_for_updates.size()) { + return; + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&UpdateEngine::UpdateCheckComplete, + base::Unretained(this), update_context)); +} + +void UpdateEngine::UpdateCheckComplete( + scoped_refptr<UpdateContext> update_context) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(update_context); + + for (const auto& id : update_context->components_to_check_for_updates) + update_context->component_queue.push(id); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent, + base::Unretained(this), update_context)); +} + +void UpdateEngine::HandleComponent( + scoped_refptr<UpdateContext> update_context) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(update_context); + + auto& queue = update_context->component_queue; + + if (queue.empty()) { + const Error error = update_context->update_check_error + ? Error::UPDATE_CHECK_ERROR + : Error::NONE; + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&UpdateEngine::UpdateComplete, base::Unretained(this), + update_context, error)); + return; + } + + const auto& id = queue.front(); + DCHECK_EQ(1u, update_context->components.count(id)); + const auto& component = update_context->components.at(id); + DCHECK(component); + + auto& next_update_delay = update_context->next_update_delay; + if (!next_update_delay.is_zero() && component->IsUpdateAvailable()) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::BindOnce(&UpdateEngine::HandleComponent, base::Unretained(this), + update_context), + next_update_delay); + next_update_delay = base::TimeDelta(); + + notify_observers_callback_.Run( + UpdateClient::Observer::Events::COMPONENT_WAIT, id); + return; + } + + component->Handle(base::BindOnce(&UpdateEngine::HandleComponentComplete, + base::Unretained(this), update_context)); +} + +void UpdateEngine::HandleComponentComplete( + scoped_refptr<UpdateContext> update_context) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(update_context); + + auto& queue = update_context->component_queue; + DCHECK(!queue.empty()); + + const auto& id = queue.front(); + DCHECK_EQ(1u, update_context->components.count(id)); + const auto& component = update_context->components.at(id); + DCHECK(component); + + if (component->IsHandled()) { + update_context->next_update_delay = component->GetUpdateDuration(); + + if (!component->events().empty()) { + ping_manager_->SendPing(*component, + base::BindOnce([](int, const std::string&) {})); + } + + queue.pop(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent, + base::Unretained(this), update_context)); +} + +void UpdateEngine::UpdateComplete(scoped_refptr<UpdateContext> update_context, + Error error) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(update_context); + + const auto num_erased = update_contexts_.erase(update_context->session_id); + DCHECK_EQ(1u, num_erased); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(update_context->callback), error)); +} + +bool UpdateEngine::GetUpdateState(const std::string& id, + CrxUpdateItem* update_item) { + DCHECK(thread_checker_.CalledOnValidThread()); + for (const auto& context : update_contexts_) { + const auto& components = context.second->components; + const auto it = components.find(id); + if (it != components.end()) { + *update_item = it->second->GetCrxUpdateItem(); + return true; + } + } + return false; +} + +bool UpdateEngine::IsThrottled(bool is_foreground) const { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (is_foreground || throttle_updates_until_.is_null()) + return false; + + const auto now(base::TimeTicks::Now()); + + // Throttle the calls in the interval (t - 1 day, t) to limit the effect of + // unset clocks or clock drift. + return throttle_updates_until_ - base::TimeDelta::FromDays(1) < now && + now < throttle_updates_until_; +} + +void UpdateEngine::SendUninstallPing(const std::string& id, + const base::Version& version, + int reason, + Callback callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + const auto update_context = base::MakeRefCounted<UpdateContext>( + config_, false, std::vector<std::string>{id}, + UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(), + std::move(callback), nullptr); + DCHECK(!update_context->session_id.empty()); + + const auto result = update_contexts_.insert( + std::make_pair(update_context->session_id, update_context)); + DCHECK(result.second); + + DCHECK(update_context); + DCHECK_EQ(1u, update_context->ids.size()); + DCHECK_EQ(1u, update_context->components.count(id)); + const auto& component = update_context->components.at(id); + + component->Uninstall(version, reason); + + update_context->component_queue.push(id); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent, + base::Unretained(this), update_context)); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/update_engine.h b/src/updater_components/update_client/update_engine.h new file mode 100644 index 0000000..9601873 --- /dev/null +++ b/src/updater_components/update_client/update_engine.h
@@ -0,0 +1,201 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_ENGINE_H_ +#define COMPONENTS_UPDATE_CLIENT_UPDATE_ENGINE_H_ + +#include <list> +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/containers/queue.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/optional.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "components/update_client/component.h" +#include "components/update_client/crx_downloader.h" +#include "components/update_client/crx_update_item.h" +#include "components/update_client/ping_manager.h" +#include "components/update_client/update_checker.h" +#include "components/update_client/update_client.h" + +namespace base { +class TimeTicks; +} // namespace base + +namespace update_client { + +class Configurator; +struct UpdateContext; + +// Handles updates for a group of components. Updates for different groups +// are run concurrently but within the same group of components, updates are +// applied one at a time. +class UpdateEngine : public base::RefCounted<UpdateEngine> { + public: + using Callback = base::OnceCallback<void(Error error)>; + using NotifyObserversCallback = + base::Callback<void(UpdateClient::Observer::Events event, + const std::string& id)>; + using CrxDataCallback = UpdateClient::CrxDataCallback; + + UpdateEngine(scoped_refptr<Configurator> config, + UpdateChecker::Factory update_checker_factory, + CrxDownloader::Factory crx_downloader_factory, + scoped_refptr<PingManager> ping_manager, + const NotifyObserversCallback& notify_observers_callback); + + // Returns true and the state of the component identified by |id|, if the + // component is found in any update context. Returns false if the component + // is not found. + bool GetUpdateState(const std::string& id, CrxUpdateItem* update_state); + + void Update(bool is_foreground, + const std::vector<std::string>& ids, + UpdateClient::CrxDataCallback crx_data_callback, + Callback update_callback); + + void SendUninstallPing(const std::string& id, + const base::Version& version, + int reason, + Callback update_callback); + + private: + friend class base::RefCounted<UpdateEngine>; + ~UpdateEngine(); + + using UpdateContexts = std::map<std::string, scoped_refptr<UpdateContext>>; + + void UpdateComplete(scoped_refptr<UpdateContext> update_context, Error error); + + void ComponentCheckingForUpdatesStart( + scoped_refptr<UpdateContext> update_context, + const std::string& id); + void ComponentCheckingForUpdatesComplete( + scoped_refptr<UpdateContext> update_context); + void UpdateCheckComplete(scoped_refptr<UpdateContext> update_context); + + void DoUpdateCheck(scoped_refptr<UpdateContext> update_context); + void UpdateCheckResultsAvailable( + scoped_refptr<UpdateContext> update_context, + const base::Optional<ProtocolParser::Results>& results, + ErrorCategory error_category, + int error, + int retry_after_sec); + + void HandleComponent(scoped_refptr<UpdateContext> update_context); + void HandleComponentComplete(scoped_refptr<UpdateContext> update_context); + + // Returns true if the update engine rejects this update call because it + // occurs too soon. + bool IsThrottled(bool is_foreground) const; + + base::ThreadChecker thread_checker_; + scoped_refptr<Configurator> config_; + UpdateChecker::Factory update_checker_factory_; + CrxDownloader::Factory crx_downloader_factory_; + scoped_refptr<PingManager> ping_manager_; + std::unique_ptr<PersistedData> metadata_; + + // Called when CRX state changes occur. + const NotifyObserversCallback notify_observers_callback_; + + // Contains the contexts associated with each update in progress. + UpdateContexts update_contexts_; + + // Implements a rate limiting mechanism for background update checks. Has the + // effect of rejecting the update call if the update call occurs before + // a certain time, which is negotiated with the server as part of the + // update protocol. See the comments for X-Retry-After header. + base::TimeTicks throttle_updates_until_; + + DISALLOW_COPY_AND_ASSIGN(UpdateEngine); +}; + +// Describes a group of components which are installed or updated together. +struct UpdateContext : public base::RefCounted<UpdateContext> { + UpdateContext( + scoped_refptr<Configurator> config, + bool is_foreground, + const std::vector<std::string>& ids, + UpdateClient::CrxDataCallback crx_data_callback, + const UpdateEngine::NotifyObserversCallback& notify_observers_callback, + UpdateEngine::Callback callback, + CrxDownloader::Factory crx_downloader_factory); + + scoped_refptr<Configurator> config; + + // True if the component is updated as a result of user interaction. + bool is_foreground = false; + + // True if the component updates are enabled in this context. + const bool enabled_component_updates; + + // Contains the ids of all CRXs in this context in the order specified + // by the caller of |UpdateClient::Update| or |UpdateClient:Install|. + const std::vector<std::string> ids; + + // Contains the map of ids to components for all the CRX in this context. + IdToComponentPtrMap components; + + // Called before an update check, when update metadata is needed. + UpdateEngine::CrxDataCallback crx_data_callback; + + // Called when there is a state change for any update in this context. + const UpdateEngine::NotifyObserversCallback notify_observers_callback; + + // Called when the all updates associated with this context have completed. + UpdateEngine::Callback callback; + + // Creates instances of CrxDownloader; + CrxDownloader::Factory crx_downloader_factory; + + std::unique_ptr<UpdateChecker> update_checker; + + // The time in seconds to wait until doing further update checks. + int retry_after_sec = 0; + + // Contains the ids of the components to check for updates. It is possible + // for a component to be uninstalled after it has been added in this context + // but before an update check is made. When this happens, the component won't + // have a CrxComponent instance, therefore, it can't be included in an + // update check. + std::vector<std::string> components_to_check_for_updates; + + // The error reported by the update checker. + int update_check_error = 0; + + size_t num_components_ready_to_check = 0; + size_t num_components_checked = 0; + + // Contains the ids of the components that the state machine must handle. + base::queue<std::string> component_queue; + + // The time to wait before handling the update for a component. + // The wait time is proportional with the cost incurred by updating + // the component. The more time it takes to download and apply the + // update for the current component, the longer the wait until the engine + // is handling the next component in the queue. + base::TimeDelta next_update_delay; + + // The unique session id of this context. The session id is serialized in + // every protocol request. It is also used as a key in various data stuctures + // to uniquely identify an update context. + const std::string session_id; + + private: + friend class base::RefCounted<UpdateContext>; + ~UpdateContext(); + + DISALLOW_COPY_AND_ASSIGN(UpdateContext); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_ENGINE_H_
diff --git a/src/updater_components/update_client/update_query_params.cc b/src/updater_components/update_client/update_query_params.cc new file mode 100644 index 0000000..d9e1612 --- /dev/null +++ b/src/updater_components/update_client/update_query_params.cc
@@ -0,0 +1,149 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/update_query_params.h" + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/system/sys_info.h" +#include "build/branding_buildflags.h" +#include "build/build_config.h" +#include "components/update_client/update_query_params_delegate.h" +#include "components/version_info/version_info.h" + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + +namespace update_client { + +namespace { + +const char kUnknown[] = "unknown"; + +// The request extra information is the OS and architecture, this helps +// the server select the right package to be delivered. +const char kOs[] = +#if defined(OS_MACOSX) + "mac"; +#elif defined(OS_WIN) + "win"; +#elif defined(OS_ANDROID) + "android"; +#elif defined(OS_CHROMEOS) + "cros"; +#elif defined(OS_LINUX) + "linux"; +#elif defined(OS_FUCHSIA) + "fuchsia"; +#elif defined(OS_OPENBSD) + "openbsd"; +#else +#error "unknown os" +#endif + +const char kArch[] = +#if defined(__amd64__) || defined(_WIN64) + "x64"; +#elif defined(__i386__) || defined(_WIN32) + "x86"; +#elif defined(__arm__) + "arm"; +#elif defined(__aarch64__) + "arm64"; +#elif defined(__mips__) && (__mips == 64) + "mips64el"; +#elif defined(__mips__) + "mipsel"; +#elif defined(__powerpc64__) + "ppc64"; +#else +#error "unknown arch" +#endif + +const char kChrome[] = "chrome"; + +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) +const char kCrx[] = "chromecrx"; +#else +const char kCrx[] = "chromiumcrx"; +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) + +UpdateQueryParamsDelegate* g_delegate = nullptr; + +} // namespace + +// static +std::string UpdateQueryParams::Get(ProdId prod) { + return base::StringPrintf( + "os=%s&arch=%s&os_arch=%s&nacl_arch=%s&prod=%s%s&acceptformat=crx2,crx3", + kOs, kArch, base::SysInfo().OperatingSystemArchitecture().c_str(), + GetNaclArch(), GetProdIdString(prod), + g_delegate ? g_delegate->GetExtraParams().c_str() : ""); +} + +// static +const char* UpdateQueryParams::GetProdIdString(UpdateQueryParams::ProdId prod) { + switch (prod) { + case UpdateQueryParams::CHROME: + return kChrome; + break; + case UpdateQueryParams::CRX: + return kCrx; + break; + } + return kUnknown; +} + +// static +const char* UpdateQueryParams::GetOS() { + return kOs; +} + +// static +const char* UpdateQueryParams::GetArch() { + return kArch; +} + +// static +const char* UpdateQueryParams::GetNaclArch() { +#if defined(ARCH_CPU_X86_FAMILY) +#if defined(ARCH_CPU_X86_64) + return "x86-64"; +#elif defined(OS_WIN) + bool x86_64 = (base::win::OSInfo::GetInstance()->wow64_status() == + base::win::OSInfo::WOW64_ENABLED); + return x86_64 ? "x86-64" : "x86-32"; +#else + return "x86-32"; +#endif +#elif defined(ARCH_CPU_ARMEL) + return "arm"; +#elif defined(ARCH_CPU_ARM64) + return "arm64"; +#elif defined(ARCH_CPU_MIPSEL) + return "mips32"; +#elif defined(ARCH_CPU_MIPS64EL) + return "mips64"; +#elif defined(ARCH_CPU_PPC64) + return "ppc64"; +#else +// NOTE: when adding new values here, please remember to update the +// comment in the .h file about possible return values from this function. +#error "You need to add support for your architecture here" +#endif +} + +// static +std::string UpdateQueryParams::GetProdVersion() { + return version_info::GetVersionNumber(); +} + +// static +void UpdateQueryParams::SetDelegate(UpdateQueryParamsDelegate* delegate) { + DCHECK(!g_delegate || !delegate || (delegate == g_delegate)); + g_delegate = delegate; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/update_query_params.h b/src/updater_components/update_client/update_query_params.h new file mode 100644 index 0000000..e315e14 --- /dev/null +++ b/src/updater_components/update_client/update_query_params.h
@@ -0,0 +1,63 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_H_ +#define COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_H_ + +#include <string> + +#include "base/macros.h" + +namespace update_client { + +class UpdateQueryParamsDelegate; + +// Generates a string of URL query parameters to be used when getting +// component and extension updates. These parameters generally remain +// fixed for a particular build. Embedders can use the delegate to +// define different implementations. This should be used only in the +// browser process. +class UpdateQueryParams { + public: + enum ProdId { + CHROME = 0, + CRX, + }; + + // Generates a string of URL query parameters for Omaha. Includes the + // following fields: "os", "arch", "nacl_arch", "prod", "prodchannel", + // "prodversion", and "lang" + static std::string Get(ProdId prod); + + // Returns the value we use for the "prod=" parameter. Possible return values + // include "chrome", "chromecrx", "chromiumcrx", and "unknown". + static const char* GetProdIdString(ProdId prod); + + // Returns the value we use for the "os=" parameter. Possible return values + // include: "mac", "win", "android", "cros", "linux", and "openbsd". + static const char* GetOS(); + + // Returns the value we use for the "arch=" parameter. Possible return values + // include: "x86", "x64", and "arm". + static const char* GetArch(); + + // Returns the value we use for the "nacl_arch" parameter. Note that this may + // be different from the "arch" parameter above (e.g. one may be 32-bit and + // the other 64-bit). Possible return values include: "x86-32", "x86-64", + // "arm", "mips32", and "ppc64". + static const char* GetNaclArch(); + + // Returns the current version of Chrome/Chromium. + static std::string GetProdVersion(); + + // Use this delegate. + static void SetDelegate(UpdateQueryParamsDelegate* delegate); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(UpdateQueryParams); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_H_
diff --git a/src/updater_components/update_client/update_query_params_delegate.cc b/src/updater_components/update_client/update_query_params_delegate.cc new file mode 100644 index 0000000..e14665a --- /dev/null +++ b/src/updater_components/update_client/update_query_params_delegate.cc
@@ -0,0 +1,15 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/update_query_params_delegate.h" + +namespace update_client { + +UpdateQueryParamsDelegate::UpdateQueryParamsDelegate() { +} + +UpdateQueryParamsDelegate::~UpdateQueryParamsDelegate() { +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/update_query_params_delegate.h b/src/updater_components/update_client/update_query_params_delegate.h new file mode 100644 index 0000000..6230e2e --- /dev/null +++ b/src/updater_components/update_client/update_query_params_delegate.h
@@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_DELEGATE_H_ +#define COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_DELEGATE_H_ + +#include <string> + +#include "base/macros.h" + +namespace update_client { + +// Embedders can specify an UpdateQueryParamsDelegate to provide additional +// custom parameters. If not specified (Set is never called), no additional +// parameters are added. +class UpdateQueryParamsDelegate { + public: + UpdateQueryParamsDelegate(); + virtual ~UpdateQueryParamsDelegate(); + + // Returns additional parameters, if any. If there are any parameters, the + // string should begin with a & character. + virtual std::string GetExtraParams() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(UpdateQueryParamsDelegate); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_DELEGATE_H_
diff --git a/src/updater_components/update_client/update_query_params_unittest.cc b/src/updater_components/update_client/update_query_params_unittest.cc new file mode 100644 index 0000000..e7ee538 --- /dev/null +++ b/src/updater_components/update_client/update_query_params_unittest.cc
@@ -0,0 +1,68 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/update_query_params.h" +#include "base/strings/stringprintf.h" +#include "base/system/sys_info.h" +#include "components/update_client/update_query_params_delegate.h" +#include "components/version_info/version_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::StringPrintf; + +namespace update_client { + +namespace { + +bool Contains(const std::string& source, const std::string& target) { + return source.find(target) != std::string::npos; +} + +class TestUpdateQueryParamsDelegate : public UpdateQueryParamsDelegate { + std::string GetExtraParams() override { return "&cat=dog"; } +}; + +} // namespace + +void TestParams(UpdateQueryParams::ProdId prod_id, bool extra_params) { + std::string params = UpdateQueryParams::Get(prod_id); + + // This doesn't so much test what the values are (since that would be an + // almost exact duplication of code with update_query_params.cc, and wouldn't + // really test anything) as it is a verification that all the params are + // present in the generated string. + EXPECT_TRUE( + Contains(params, StringPrintf("os=%s", UpdateQueryParams::GetOS()))); + EXPECT_TRUE( + Contains(params, StringPrintf("arch=%s", UpdateQueryParams::GetArch()))); + EXPECT_TRUE(Contains( + params, + StringPrintf("os_arch=%s", + base::SysInfo().OperatingSystemArchitecture().c_str()))); + EXPECT_TRUE(Contains( + params, + StringPrintf("prod=%s", UpdateQueryParams::GetProdIdString(prod_id)))); + if (extra_params) + EXPECT_TRUE(Contains(params, "cat=dog")); +} + +void TestProdVersion() { + EXPECT_EQ(version_info::GetVersionNumber(), + UpdateQueryParams::GetProdVersion()); +} + +TEST(UpdateQueryParamsTest, GetParams) { + TestProdVersion(); + + TestParams(UpdateQueryParams::CRX, false); + TestParams(UpdateQueryParams::CHROME, false); + + TestUpdateQueryParamsDelegate delegate; + UpdateQueryParams::SetDelegate(&delegate); + + TestParams(UpdateQueryParams::CRX, true); + TestParams(UpdateQueryParams::CHROME, true); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/updater_state.cc b/src/updater_components/update_client/updater_state.cc new file mode 100644 index 0000000..5d05c86 --- /dev/null +++ b/src/updater_components/update_client/updater_state.cc
@@ -0,0 +1,102 @@ + +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/updater_state.h" + +#include <utility> + +#include "base/enterprise_util.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "build/branding_buildflags.h" +#include "build/build_config.h" + +namespace update_client { + +// The value of this constant does not reflect its name (i.e. "domainjoined" +// vs something like "isenterprisemanaged") because it is used with omaha. +// After discussion with omaha team it was decided to leave the value as is to +// keep continuity with previous chrome versions. +const char UpdaterState::kIsEnterpriseManaged[] = "domainjoined"; + +UpdaterState::UpdaterState(bool is_machine) : is_machine_(is_machine) {} + +UpdaterState::~UpdaterState() {} + +std::unique_ptr<UpdaterState::Attributes> UpdaterState::GetState( + bool is_machine) { +#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) + UpdaterState updater_state(is_machine); + updater_state.ReadState(); + return std::make_unique<Attributes>(updater_state.BuildAttributes()); +#else + return nullptr; +#endif // OS_WIN or Mac +} + +#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +void UpdaterState::ReadState() { + is_enterprise_managed_ = base::IsMachineExternallyManaged(); + +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + updater_name_ = GetUpdaterName(); + updater_version_ = GetUpdaterVersion(is_machine_); + last_autoupdate_started_ = GetUpdaterLastStartedAU(is_machine_); + last_checked_ = GetUpdaterLastChecked(is_machine_); + is_autoupdate_check_enabled_ = IsAutoupdateCheckEnabled(); + update_policy_ = GetUpdatePolicy(); +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) +} +#endif // OS_WIN or Mac + +UpdaterState::Attributes UpdaterState::BuildAttributes() const { + Attributes attributes; + +#if defined(OS_WIN) + // Only Windows implements this attribute in a meaningful way. + attributes["ismachine"] = is_machine_ ? "1" : "0"; +#endif // OS_WIN + attributes[kIsEnterpriseManaged] = is_enterprise_managed_ ? "1" : "0"; + + attributes["name"] = updater_name_; + + if (updater_version_.IsValid()) + attributes["version"] = updater_version_.GetString(); + + const base::Time now = base::Time::NowFromSystemTime(); + if (!last_autoupdate_started_.is_null()) + attributes["laststarted"] = + NormalizeTimeDelta(now - last_autoupdate_started_); + if (!last_checked_.is_null()) + attributes["lastchecked"] = NormalizeTimeDelta(now - last_checked_); + + attributes["autoupdatecheckenabled"] = + is_autoupdate_check_enabled_ ? "1" : "0"; + + DCHECK((update_policy_ >= 0 && update_policy_ <= 3) || update_policy_ == -1); + attributes["updatepolicy"] = base::NumberToString(update_policy_); + + return attributes; +} + +std::string UpdaterState::NormalizeTimeDelta(const base::TimeDelta& delta) { + const base::TimeDelta two_weeks = base::TimeDelta::FromDays(14); + const base::TimeDelta two_months = base::TimeDelta::FromDays(60); + + std::string val; // Contains the value to return in hours. + if (delta <= two_weeks) { + val = "0"; + } else if (two_weeks < delta && delta <= two_months) { + val = "408"; // 2 weeks in hours. + } else { + val = "1344"; // 2*28 days in hours. + } + + DCHECK(!val.empty()); + return val; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/updater_state.h b/src/updater_components/update_client/updater_state.h new file mode 100644 index 0000000..ec11d4a --- /dev/null +++ b/src/updater_components/update_client/updater_state.h
@@ -0,0 +1,69 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UPDATER_STATE_H_ +#define COMPONENTS_UPDATE_CLIENT_UPDATER_STATE_H_ + +#include <map> +#include <memory> +#include <string> + +#include "base/gtest_prod_util.h" +#include "base/time/time.h" +#include "base/version.h" + +namespace update_client { + +class UpdaterState { + public: + using Attributes = std::map<std::string, std::string>; + + static const char kIsEnterpriseManaged[]; + + // Returns a map of items representing the state of an updater. + // If |is_machine| is true, this indicates that the updater state corresponds + // to the machine instance of the updater. Returns nullptr on + // the platforms and builds where this feature is not supported. + static std::unique_ptr<Attributes> GetState(bool is_machine); + + ~UpdaterState(); + + private: + FRIEND_TEST_ALL_PREFIXES(UpdaterStateTest, Serialize); + + explicit UpdaterState(bool is_machine); + + // This function is best-effort. It updates the class members with + // the relevant values that could be retrieved. + void ReadState(); + + // Builds the map of state attributes by serializing this object state. + Attributes BuildAttributes() const; + + static std::string GetUpdaterName(); + static base::Version GetUpdaterVersion(bool is_machine); + static bool IsAutoupdateCheckEnabled(); + static base::Time GetUpdaterLastStartedAU(bool is_machine); + static base::Time GetUpdaterLastChecked(bool is_machine); + + static int GetUpdatePolicy(); + + static std::string NormalizeTimeDelta(const base::TimeDelta& delta); + + // True if the Omaha updater is installed per-machine. + // The MacOS implementation ignores the value of this member but this may + // change in the future. + bool is_machine_; + std::string updater_name_; + base::Version updater_version_; + base::Time last_autoupdate_started_; + base::Time last_checked_; + bool is_enterprise_managed_ = false; + bool is_autoupdate_check_enabled_ = false; + int update_policy_ = 0; +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UPDATER_STATE_H_
diff --git a/src/updater_components/update_client/updater_state_mac.mm b/src/updater_components/update_client/updater_state_mac.mm new file mode 100644 index 0000000..a7bceb6 --- /dev/null +++ b/src/updater_components/update_client/updater_state_mac.mm
@@ -0,0 +1,119 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <Foundation/Foundation.h> + +#include "base/enterprise_util.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_nsautorelease_pool.h" +#include "base/mac/scoped_nsobject.h" +#include "base/strings/sys_string_conversions.h" +#include "base/version.h" +#include "components/update_client/updater_state.h" + +namespace update_client { + +namespace { + +const base::FilePath::CharType kKeystonePlist[] = FILE_PATH_LITERAL( + "Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/" + "Contents/Info.plist"); + +// Gets a value from the updater settings. Returns a retained object. +// T should be a toll-free Foundation framework type. See Apple's +// documentation for toll-free bridging. +template<class T> +base::scoped_nsobject<T> GetUpdaterSettingsValue(NSString* value_name) { + CFStringRef app_id = CFSTR("com.google.Keystone.Agent"); + base::ScopedCFTypeRef<CFPropertyListRef> plist( + CFPreferencesCopyAppValue(base::mac::NSToCFCast(value_name), app_id)); + return base::scoped_nsobject<T>( + base::mac::ObjCCastStrict<T>(static_cast<id>(plist.get())), + base::scoped_policy::RETAIN); +} + +base::Time GetUpdaterSettingsTime(NSString* value_name) { + base::scoped_nsobject<NSDate> date = + GetUpdaterSettingsValue<NSDate>(value_name); + base::Time result = + base::Time::FromCFAbsoluteTime([date timeIntervalSinceReferenceDate]); + + return result; +} + +base::Version GetVersionFromPlist(const base::FilePath& info_plist) { + base::mac::ScopedNSAutoreleasePool scoped_pool; + NSData* data = + [NSData dataWithContentsOfFile: + base::mac::FilePathToNSString(info_plist)]; + if ([data length] == 0) { + return base::Version(); + } + NSDictionary* all_keys = base::mac::ObjCCastStrict<NSDictionary>( + [NSPropertyListSerialization propertyListWithData:data + options:NSPropertyListImmutable + format:nil + error:nil]); + if (all_keys == nil) { + return base::Version(); + } + CFStringRef version = + base::mac::GetValueFromDictionary<CFStringRef>( + base::mac::NSToCFCast(all_keys), + kCFBundleVersionKey); + if (version == NULL) { + return base::Version(); + } + return base::Version(base::SysCFStringRefToUTF8(version)); +} + +} // namespace + +std::string UpdaterState::GetUpdaterName() { + return std::string("Keystone"); +} + +base::Version UpdaterState::GetUpdaterVersion(bool /*is_machine*/) { + // System Keystone trumps user one, so check this one first + base::FilePath local_library; + bool success = base::mac::GetLocalDirectory(NSLibraryDirectory, + &local_library); + DCHECK(success); + base::FilePath system_bundle_plist = local_library.Append(kKeystonePlist); + base::Version system_keystone = GetVersionFromPlist(system_bundle_plist); + if (system_keystone.IsValid()) { + return system_keystone; + } + + base::FilePath user_bundle_plist = + base::mac::GetUserLibraryPath().Append(kKeystonePlist); + return GetVersionFromPlist(user_bundle_plist); +} + +base::Time UpdaterState::GetUpdaterLastStartedAU(bool /*is_machine*/) { + return GetUpdaterSettingsTime(@"lastCheckStartDate"); +} + +base::Time UpdaterState::GetUpdaterLastChecked(bool /*is_machine*/) { + return GetUpdaterSettingsTime(@"lastServerCheckDate"); +} + +bool UpdaterState::IsAutoupdateCheckEnabled() { + // Auto-update check period override (in seconds). + // Applies only to older versions of Keystone. + base::scoped_nsobject<NSNumber> timeInterval = + GetUpdaterSettingsValue<NSNumber>(@"checkInterval"); + if (!timeInterval.get()) return true; + int value = [timeInterval intValue]; + + return 0 < value && value < (24 * 60 * 60); +} + +int UpdaterState::GetUpdatePolicy() { + return -1; // Keystone does not support update policies. +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/updater_state_unittest.cc b/src/updater_components/update_client/updater_state_unittest.cc new file mode 100644 index 0000000..e916adf --- /dev/null +++ b/src/updater_components/update_client/updater_state_unittest.cc
@@ -0,0 +1,122 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/updater_state.h" + +#include "base/macros.h" +#include "base/time/time.h" +#include "base/version.h" +#include "build/branding_buildflags.h" +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace update_client { + +class UpdaterStateTest : public testing::Test { + public: + UpdaterStateTest() {} + ~UpdaterStateTest() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(UpdaterStateTest); +}; + +TEST_F(UpdaterStateTest, Serialize) { + UpdaterState updater_state(false); + + updater_state.updater_name_ = "the updater"; + updater_state.updater_version_ = base::Version("1.0"); + updater_state.last_autoupdate_started_ = base::Time::NowFromSystemTime(); + updater_state.last_checked_ = base::Time::NowFromSystemTime(); + updater_state.is_enterprise_managed_ = false; + updater_state.is_autoupdate_check_enabled_ = true; + updater_state.update_policy_ = 1; + + auto attributes = updater_state.BuildAttributes(); + + // Sanity check all members. + EXPECT_STREQ("the updater", attributes.at("name").c_str()); + EXPECT_STREQ("1.0", attributes.at("version").c_str()); + EXPECT_STREQ("0", attributes.at("laststarted").c_str()); + EXPECT_STREQ("0", attributes.at("lastchecked").c_str()); + EXPECT_STREQ("0", attributes.at("domainjoined").c_str()); + EXPECT_STREQ("1", attributes.at("autoupdatecheckenabled").c_str()); + EXPECT_STREQ("1", attributes.at("updatepolicy").c_str()); + +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) +#if defined(OS_WIN) + // The value of "ismachine". + EXPECT_STREQ("0", UpdaterState::GetState(false)->at("ismachine").c_str()); + EXPECT_STREQ("1", UpdaterState::GetState(true)->at("ismachine").c_str()); + + // The name of the Windows updater for Chrome. + EXPECT_STREQ("Omaha", UpdaterState::GetState(false)->at("name").c_str()); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + // MacOS does not serialize "ismachine". + EXPECT_EQ(0UL, UpdaterState::GetState(false)->count("ismachine")); + EXPECT_EQ(0UL, UpdaterState::GetState(true)->count("ismachine")); + EXPECT_STREQ("Keystone", UpdaterState::GetState(false)->at("name").c_str()); +#endif // OS_WIN +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) + + // Tests some of the remaining values. + updater_state = UpdaterState(false); + + // Don't serialize an invalid version if it could not be read. + updater_state.updater_version_ = base::Version(); + attributes = updater_state.BuildAttributes(); + EXPECT_EQ(0u, attributes.count("version")); + + updater_state.updater_version_ = base::Version("0.0.0.0"); + attributes = updater_state.BuildAttributes(); + EXPECT_STREQ("0.0.0.0", attributes.at("version").c_str()); + + updater_state.last_autoupdate_started_ = + base::Time::NowFromSystemTime() - base::TimeDelta::FromDays(15); + attributes = updater_state.BuildAttributes(); + EXPECT_STREQ("408", attributes.at("laststarted").c_str()); + + updater_state.last_autoupdate_started_ = + base::Time::NowFromSystemTime() - base::TimeDelta::FromDays(90); + attributes = updater_state.BuildAttributes(); + EXPECT_STREQ("1344", attributes.at("laststarted").c_str()); + + // Don't serialize the time if it could not be read. + updater_state.last_autoupdate_started_ = base::Time(); + attributes = updater_state.BuildAttributes(); + EXPECT_EQ(0u, attributes.count("laststarted")); + + updater_state.last_checked_ = + base::Time::NowFromSystemTime() - base::TimeDelta::FromDays(15); + attributes = updater_state.BuildAttributes(); + EXPECT_STREQ("408", attributes.at("lastchecked").c_str()); + + updater_state.last_checked_ = + base::Time::NowFromSystemTime() - base::TimeDelta::FromDays(90); + attributes = updater_state.BuildAttributes(); + EXPECT_STREQ("1344", attributes.at("lastchecked").c_str()); + + // Don't serialize the time if it could not be read (the value is invalid). + updater_state.last_checked_ = base::Time(); + attributes = updater_state.BuildAttributes(); + EXPECT_EQ(0u, attributes.count("lastchecked")); + + updater_state.is_enterprise_managed_ = true; + attributes = updater_state.BuildAttributes(); + EXPECT_STREQ("1", attributes.at("domainjoined").c_str()); + + updater_state.is_autoupdate_check_enabled_ = false; + attributes = updater_state.BuildAttributes(); + EXPECT_STREQ("0", attributes.at("autoupdatecheckenabled").c_str()); + + updater_state.update_policy_ = 0; + attributes = updater_state.BuildAttributes(); + EXPECT_STREQ("0", attributes.at("updatepolicy").c_str()); + + updater_state.update_policy_ = -1; + attributes = updater_state.BuildAttributes(); + EXPECT_STREQ("-1", attributes.at("updatepolicy").c_str()); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/updater_state_win.cc b/src/updater_components/update_client/updater_state_win.cc new file mode 100644 index 0000000..89c2c18 --- /dev/null +++ b/src/updater_components/update_client/updater_state_win.cc
@@ -0,0 +1,136 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/updater_state.h" + +#include <windows.h> + +#include <string> +#include <utility> + +#include "base/enterprise_util.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/win/registry.h" +#include "base/win/win_util.h" + +// TODO(sorin): implement this in terms of +// chrome/installer/util/google_update_settings (crbug.com/615187). + +namespace update_client { + +namespace { + +// Google Update group policy settings. +const wchar_t kGoogleUpdatePoliciesKey[] = + L"SOFTWARE\\Policies\\Google\\Update"; +const wchar_t kCheckPeriodOverrideMinutes[] = L"AutoUpdateCheckPeriodMinutes"; +const wchar_t kUpdatePolicyValue[] = L"UpdateDefault"; +const wchar_t kChromeUpdatePolicyOverride[] = + L"Update{8A69D345-D564-463C-AFF1-A69D9E530F96}"; + +// Don't allow update periods longer than six weeks (Chrome release cadence). +const int kCheckPeriodOverrideMinutesMax = 60 * 24 * 7 * 6; + +// Google Update registry settings. +const wchar_t kRegPathGoogleUpdate[] = L"Software\\Google\\Update"; +const wchar_t kRegPathClientsGoogleUpdate[] = + L"Software\\Google\\Update\\Clients\\" + L"{430FD4D0-B729-4F61-AA34-91526481799D}"; +const wchar_t kRegValueGoogleUpdatePv[] = L"pv"; +const wchar_t kRegValueLastStartedAU[] = L"LastStartedAU"; +const wchar_t kRegValueLastChecked[] = L"LastChecked"; + +base::Time GetUpdaterTimeValue(bool is_machine, const wchar_t* value_name) { + const HKEY root_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + base::win::RegKey update_key; + + if (update_key.Open(root_key, kRegPathGoogleUpdate, + KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) { + DWORD value(0); + if (update_key.ReadValueDW(value_name, &value) == ERROR_SUCCESS) { + return base::Time::FromTimeT(value); + } + } + + return base::Time(); +} + +} // namespace + +std::string UpdaterState::GetUpdaterName() { + return std::string("Omaha"); +} + +base::Version UpdaterState::GetUpdaterVersion(bool is_machine) { + const HKEY root_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + base::string16 version; + base::win::RegKey key; + + if (key.Open(root_key, kRegPathClientsGoogleUpdate, + KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS && + key.ReadValue(kRegValueGoogleUpdatePv, &version) == ERROR_SUCCESS) { + return base::Version(base::UTF16ToUTF8(version)); + } + + return base::Version(); +} + +base::Time UpdaterState::GetUpdaterLastStartedAU(bool is_machine) { + return GetUpdaterTimeValue(is_machine, kRegValueLastStartedAU); +} + +base::Time UpdaterState::GetUpdaterLastChecked(bool is_machine) { + return GetUpdaterTimeValue(is_machine, kRegValueLastChecked); +} + +bool UpdaterState::IsAutoupdateCheckEnabled() { + // Check the auto-update check period override. If it is 0 or exceeds the + // maximum timeout, then for all intents and purposes auto updates are + // disabled. + base::win::RegKey policy_key; + DWORD value = 0; + if (policy_key.Open(HKEY_LOCAL_MACHINE, kGoogleUpdatePoliciesKey, + KEY_QUERY_VALUE) == ERROR_SUCCESS && + policy_key.ReadValueDW(kCheckPeriodOverrideMinutes, &value) == + ERROR_SUCCESS && + (value == 0 || value > kCheckPeriodOverrideMinutesMax)) { + return false; + } + + return true; +} + +// Returns -1 if the policy is not found or the value was invalid. Otherwise, +// returns a value in the [0, 3] range, representing the value of the +// Chrome update group policy. +int UpdaterState::GetUpdatePolicy() { + const int kMaxUpdatePolicyValue = 3; + + base::win::RegKey policy_key; + + if (policy_key.Open(HKEY_LOCAL_MACHINE, kGoogleUpdatePoliciesKey, + KEY_QUERY_VALUE) != ERROR_SUCCESS) { + return -1; + } + + DWORD value = 0; + // First try to read the Chrome-specific override. + if (policy_key.ReadValueDW(kChromeUpdatePolicyOverride, &value) == + ERROR_SUCCESS && + value <= kMaxUpdatePolicyValue) { + return value; + } + + // Try to read default override. + if (policy_key.ReadValueDW(kUpdatePolicyValue, &value) == ERROR_SUCCESS && + value <= kMaxUpdatePolicyValue) { + return value; + } + + return -1; +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/url_fetcher_downloader.cc b/src/updater_components/update_client/url_fetcher_downloader.cc new file mode 100644 index 0000000..eaf78ad --- /dev/null +++ b/src/updater_components/update_client/url_fetcher_downloader.cc
@@ -0,0 +1,167 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/url_fetcher_downloader.h" + +#include <stdint.h> +#include <utility> + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/sequenced_task_runner.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#include "components/update_client/network.h" +#include "components/update_client/utils.h" +#include "url/gurl.h" + +namespace { + +constexpr base::TaskTraits kTaskTraits = { + base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT, + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}; + +} // namespace + +namespace update_client { + +UrlFetcherDownloader::UrlFetcherDownloader( + std::unique_ptr<CrxDownloader> successor, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) + : CrxDownloader(std::move(successor)), + network_fetcher_factory_(network_fetcher_factory) {} + +UrlFetcherDownloader::~UrlFetcherDownloader() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +} + +void UrlFetcherDownloader::DoStartDownload(const GURL& url) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + base::PostTaskAndReply( + FROM_HERE, kTaskTraits, + base::BindOnce(&UrlFetcherDownloader::CreateDownloadDir, + base::Unretained(this)), + base::BindOnce(&UrlFetcherDownloader::StartURLFetch, + base::Unretained(this), url)); +} + +void UrlFetcherDownloader::CreateDownloadDir() { + base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_url_fetcher_"), + &download_dir_); +} + +void UrlFetcherDownloader::StartURLFetch(const GURL& url) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (download_dir_.empty()) { + Result result; + result.error = -1; + + DownloadMetrics download_metrics; + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kUrlFetcher; + download_metrics.error = -1; + download_metrics.downloaded_bytes = -1; + download_metrics.total_bytes = -1; + download_metrics.download_time_ms = 0; + + main_task_runner()->PostTask( + FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete, + base::Unretained(this), false, result, + download_metrics)); + return; + } + + const auto file_path = download_dir_.AppendASCII(url.ExtractFileName()); + network_fetcher_ = network_fetcher_factory_->Create(); + network_fetcher_->DownloadToFile( + url, file_path, + base::BindOnce(&UrlFetcherDownloader::OnResponseStarted, + base::Unretained(this)), + base::BindRepeating(&UrlFetcherDownloader::OnDownloadProgress, + base::Unretained(this)), + base::BindOnce(&UrlFetcherDownloader::OnNetworkFetcherComplete, + base::Unretained(this))); + + download_start_time_ = base::TimeTicks::Now(); +} + +void UrlFetcherDownloader::OnNetworkFetcherComplete(base::FilePath file_path, + int net_error, + int64_t content_size) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + const base::TimeTicks download_end_time(base::TimeTicks::Now()); + const base::TimeDelta download_time = + download_end_time >= download_start_time_ + ? download_end_time - download_start_time_ + : base::TimeDelta(); + + // Consider a 5xx response from the server as an indication to terminate + // the request and avoid overloading the server in this case. + // is not accepting requests for the moment. + int error = -1; + if (!file_path.empty() && response_code_ == 200) { + DCHECK_EQ(0, net_error); + error = 0; + } else if (response_code_ != -1) { + error = response_code_; + } else { + error = net_error; + } + + const bool is_handled = error == 0 || IsHttpServerError(error); + + Result result; + result.error = error; + if (!error) { + result.response = file_path; + } + + DownloadMetrics download_metrics; + download_metrics.url = url(); + download_metrics.downloader = DownloadMetrics::kUrlFetcher; + download_metrics.error = error; + // Tests expected -1, in case of failures and no content is available. + download_metrics.downloaded_bytes = error ? -1 : content_size; + download_metrics.total_bytes = total_bytes_; + download_metrics.download_time_ms = download_time.InMilliseconds(); + + VLOG(1) << "Downloaded " << content_size << " bytes in " + << download_time.InMilliseconds() << "ms from " << final_url_.spec() + << " to " << result.response.value(); + + // Delete the download directory in the error cases. + if (error && !download_dir_.empty()) + base::PostTask( + FROM_HERE, kTaskTraits, + base::BindOnce(IgnoreResult(&base::DeleteFile), download_dir_, true)); + + main_task_runner()->PostTask( + FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete, + base::Unretained(this), is_handled, result, + download_metrics)); +} + +// This callback is used to indicate that a download has been started. +void UrlFetcherDownloader::OnResponseStarted(const GURL& final_url, + int response_code, + int64_t content_length) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + VLOG(1) << "url fetcher response started for: " << final_url.spec(); + + final_url_ = final_url; + response_code_ = response_code; + total_bytes_ = content_length; +} + +void UrlFetcherDownloader::OnDownloadProgress(int64_t current) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + CrxDownloader::OnDownloadProgress(); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/url_fetcher_downloader.h b/src/updater_components/update_client/url_fetcher_downloader.h new file mode 100644 index 0000000..a9330b8 --- /dev/null +++ b/src/updater_components/update_client/url_fetcher_downloader.h
@@ -0,0 +1,65 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_URL_FETCHER_DOWNLOADER_H_ +#define COMPONENTS_UPDATE_CLIENT_URL_FETCHER_DOWNLOADER_H_ + +#include <stdint.h> + +#include <memory> + +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "components/update_client/crx_downloader.h" + +namespace update_client { + +class NetworkFetcher; +class NetworkFetcherFactory; + +// Implements a CRX downloader using a NetworkFetcher object. +class UrlFetcherDownloader : public CrxDownloader { + public: + UrlFetcherDownloader( + std::unique_ptr<CrxDownloader> successor, + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory); + ~UrlFetcherDownloader() override; + + private: + // Overrides for CrxDownloader. + void DoStartDownload(const GURL& url) override; + + void CreateDownloadDir(); + void StartURLFetch(const GURL& url); + void OnNetworkFetcherComplete(base::FilePath file_path, + int net_error, + int64_t content_size); + void OnResponseStarted(const GURL& final_url, + int response_code, + int64_t content_length); + void OnDownloadProgress(int64_t content_length); + + THREAD_CHECKER(thread_checker_); + + scoped_refptr<NetworkFetcherFactory> network_fetcher_factory_; + std::unique_ptr<NetworkFetcher> network_fetcher_; + + // Contains a temporary download directory for the downloaded file. + base::FilePath download_dir_; + + base::TimeTicks download_start_time_; + + GURL final_url_; + int response_code_ = -1; + int64_t total_bytes_ = -1; + + DISALLOW_COPY_AND_ASSIGN(UrlFetcherDownloader); +}; + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_URL_FETCHER_DOWNLOADER_H_
diff --git a/src/updater_components/update_client/utils.cc b/src/updater_components/update_client/utils.cc new file mode 100644 index 0000000..4952c37 --- /dev/null +++ b/src/updater_components/update_client/utils.cc
@@ -0,0 +1,167 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/utils.h" + +#include <stddef.h> + +#include <algorithm> +#include <cmath> +#include <cstring> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/memory_mapped_file.h" +#include "base/json/json_file_value_serializer.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/values.h" +#include "components/crx_file/id_util.h" +#include "components/update_client/component.h" +#include "components/update_client/configurator.h" +#include "components/update_client/network.h" +#include "components/update_client/update_client.h" +#include "components/update_client/update_client_errors.h" +#include "crypto/secure_hash.h" +#include "crypto/sha2.h" +#include "url/gurl.h" + +namespace update_client { + +bool HasDiffUpdate(const Component& component) { + return !component.crx_diffurls().empty(); +} + +bool IsHttpServerError(int status_code) { + return 500 <= status_code && status_code < 600; +} + +bool DeleteFileAndEmptyParentDirectory(const base::FilePath& filepath) { + if (!base::DeleteFile(filepath, false)) + return false; + + const base::FilePath dirname(filepath.DirName()); + if (!base::IsDirectoryEmpty(dirname)) + return true; + + return base::DeleteFile(dirname, false); +} + +std::string GetCrxComponentID(const CrxComponent& component) { + return GetCrxIdFromPublicKeyHash(component.pk_hash); +} + +std::string GetCrxIdFromPublicKeyHash(const std::vector<uint8_t>& pk_hash) { + const std::string result = + crx_file::id_util::GenerateIdFromHash(&pk_hash[0], pk_hash.size()); + DCHECK(crx_file::id_util::IdIsValid(result)); + return result; +} + +bool VerifyFileHash256(const base::FilePath& filepath, + const std::string& expected_hash_str) { + std::vector<uint8_t> expected_hash; + if (!base::HexStringToBytes(expected_hash_str, &expected_hash) || + expected_hash.size() != crypto::kSHA256Length) { + return false; + } + + base::MemoryMappedFile mmfile; + if (!mmfile.Initialize(filepath)) + return false; + + uint8_t actual_hash[crypto::kSHA256Length] = {0}; + std::unique_ptr<crypto::SecureHash> hasher( + crypto::SecureHash::Create(crypto::SecureHash::SHA256)); + hasher->Update(mmfile.data(), mmfile.length()); + hasher->Finish(actual_hash, sizeof(actual_hash)); + + return memcmp(actual_hash, &expected_hash[0], sizeof(actual_hash)) == 0; +} + +bool IsValidBrand(const std::string& brand) { + const size_t kMaxBrandSize = 4; + if (!brand.empty() && brand.size() != kMaxBrandSize) + return false; + + return std::find_if_not(brand.begin(), brand.end(), [](char ch) { + return base::IsAsciiAlpha(ch); + }) == brand.end(); +} + +// Helper function. +// Returns true if |part| matches the expression +// ^[<special_chars>a-zA-Z0-9]{min_length,max_length}$ +bool IsValidInstallerAttributePart(const std::string& part, + const std::string& special_chars, + size_t min_length, + size_t max_length) { + if (part.size() < min_length || part.size() > max_length) + return false; + + return std::find_if_not(part.begin(), part.end(), [&special_chars](char ch) { + if (base::IsAsciiAlpha(ch) || base::IsAsciiDigit(ch)) + return true; + + for (auto c : special_chars) { + if (c == ch) + return true; + } + + return false; + }) == part.end(); +} + +// Returns true if the |name| parameter matches ^[-_a-zA-Z0-9]{1,256}$ . +bool IsValidInstallerAttributeName(const std::string& name) { + return IsValidInstallerAttributePart(name, "-_", 1, 256); +} + +// Returns true if the |value| parameter matches ^[-.,;+_=a-zA-Z0-9]{0,256}$ . +bool IsValidInstallerAttributeValue(const std::string& value) { + return IsValidInstallerAttributePart(value, "-.,;+_=", 0, 256); +} + +bool IsValidInstallerAttribute(const InstallerAttribute& attr) { + return IsValidInstallerAttributeName(attr.first) && + IsValidInstallerAttributeValue(attr.second); +} + +void RemoveUnsecureUrls(std::vector<GURL>* urls) { + DCHECK(urls); + base::EraseIf(*urls, + [](const GURL& url) { return !url.SchemeIsCryptographic(); }); +} + +CrxInstaller::Result InstallFunctionWrapper( + base::OnceCallback<bool()> callback) { + return CrxInstaller::Result(std::move(callback).Run() + ? InstallError::NONE + : InstallError::GENERIC_ERROR); +} + +// TODO(cpu): add a specific attribute check to a component json that the +// extension unpacker will reject, so that a component cannot be installed +// as an extension. +std::unique_ptr<base::DictionaryValue> ReadManifest( + const base::FilePath& unpack_path) { + base::FilePath manifest = + unpack_path.Append(FILE_PATH_LITERAL("manifest.json")); + if (!base::PathExists(manifest)) + return std::unique_ptr<base::DictionaryValue>(); + JSONFileValueDeserializer deserializer(manifest); + std::string error; + std::unique_ptr<base::Value> root = deserializer.Deserialize(nullptr, &error); + if (!root) + return std::unique_ptr<base::DictionaryValue>(); + if (!root->is_dict()) + return std::unique_ptr<base::DictionaryValue>(); + return std::unique_ptr<base::DictionaryValue>( + static_cast<base::DictionaryValue*>(root.release())); +} + +} // namespace update_client
diff --git a/src/updater_components/update_client/utils.h b/src/updater_components/update_client/utils.h new file mode 100644 index 0000000..14567e6 --- /dev/null +++ b/src/updater_components/update_client/utils.h
@@ -0,0 +1,91 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UPDATE_CLIENT_UTILS_H_ +#define COMPONENTS_UPDATE_CLIENT_UTILS_H_ + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/callback_forward.h" +#include "base/memory/ref_counted.h" +#include "components/update_client/update_client.h" + +class GURL; + +namespace base { +class DictionaryValue; +class FilePath; +} + +namespace update_client { + +class Component; +struct CrxComponent; + +// Defines a name-value pair that represents an installer attribute. +// Installer attributes are component-specific metadata, which may be serialized +// in an update check request. +using InstallerAttribute = std::pair<std::string, std::string>; + +// Returns true if the |component| contains a valid differential update url. +bool HasDiffUpdate(const Component& component); + +// Returns true if the |status_code| represents a server error 5xx. +bool IsHttpServerError(int status_code); + +// Deletes the file and its directory, if the directory is empty. If the +// parent directory is not empty, the function ignores deleting the directory. +// Returns true if the file and the empty directory are deleted. +bool DeleteFileAndEmptyParentDirectory(const base::FilePath& filepath); + +// Returns the component id of the |component|. The component id is in a +// format similar with the format of an extension id. +std::string GetCrxComponentID(const CrxComponent& component); + +// Returns a CRX id from a public key hash. +std::string GetCrxIdFromPublicKeyHash(const std::vector<uint8_t>& pk_hash); + +// Returns true if the actual SHA-256 hash of the |filepath| matches the +// |expected_hash|. +bool VerifyFileHash256(const base::FilePath& filepath, + const std::string& expected_hash); + +// Returns true if the |brand| parameter matches ^[a-zA-Z]{4}?$ . +bool IsValidBrand(const std::string& brand); + +// Returns true if the name part of the |attr| parameter matches +// ^[-_a-zA-Z0-9]{1,256}$ and the value part of the |attr| parameter +// matches ^[-.,;+_=a-zA-Z0-9]{0,256}$ . +bool IsValidInstallerAttribute(const InstallerAttribute& attr); + +// Removes the unsecure urls in the |urls| parameter. +void RemoveUnsecureUrls(std::vector<GURL>* urls); + +// Adapter function for the old definitions of CrxInstaller::Install until the +// component installer code is migrated to use a Result instead of bool. +CrxInstaller::Result InstallFunctionWrapper( + base::OnceCallback<bool()> callback); + +// Deserializes the CRX manifest. The top level must be a dictionary. +std::unique_ptr<base::DictionaryValue> ReadManifest( + const base::FilePath& unpack_path); + +// Converts a custom, specific installer error (and optionally extended error) +// to an installer result. +template <typename T> +CrxInstaller::Result ToInstallerResult(const T& error, int extended_error = 0) { + static_assert(std::is_enum<T>::value, + "Use an enum class to define custom installer errors"); + return CrxInstaller::Result( + static_cast<int>(update_client::InstallError::CUSTOM_ERROR_BASE) + + static_cast<int>(error), + extended_error); +} + +} // namespace update_client + +#endif // COMPONENTS_UPDATE_CLIENT_UTILS_H_
diff --git a/src/updater_components/update_client/utils_unittest.cc b/src/updater_components/update_client/utils_unittest.cc new file mode 100644 index 0000000..71a8b88 --- /dev/null +++ b/src/updater_components/update_client/utils_unittest.cc
@@ -0,0 +1,204 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/update_client/utils.h" + +#include <iterator> + +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "components/update_client/updater_state.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +using std::string; + +namespace { + +base::FilePath MakeTestFilePath(const char* file) { + base::FilePath path; + base::PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("components/test/data/update_client") + .AppendASCII(file); +} + +} // namespace + +namespace update_client { + +TEST(UpdateClientUtils, VerifyFileHash256) { + EXPECT_TRUE(VerifyFileHash256( + MakeTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), + std::string( + "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87"))); + + EXPECT_FALSE(VerifyFileHash256( + MakeTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), + std::string(""))); + + EXPECT_FALSE(VerifyFileHash256( + MakeTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), + std::string("abcd"))); + + EXPECT_FALSE(VerifyFileHash256( + MakeTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), + std::string( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))); +} + +// Tests that the brand matches ^[a-zA-Z]{4}?$ +TEST(UpdateClientUtils, IsValidBrand) { + // The valid brand code must be empty or exactly 4 chars long. + EXPECT_TRUE(IsValidBrand(std::string(""))); + EXPECT_TRUE(IsValidBrand(std::string("TEST"))); + EXPECT_TRUE(IsValidBrand(std::string("test"))); + EXPECT_TRUE(IsValidBrand(std::string("TEst"))); + + EXPECT_FALSE(IsValidBrand(std::string("T"))); // Too short. + EXPECT_FALSE(IsValidBrand(std::string("TE"))); // + EXPECT_FALSE(IsValidBrand(std::string("TES"))); // + EXPECT_FALSE(IsValidBrand(std::string("TESTS"))); // Too long. + EXPECT_FALSE(IsValidBrand(std::string("TES1"))); // Has digit. + EXPECT_FALSE(IsValidBrand(std::string(" TES"))); // Begins with white space. + EXPECT_FALSE(IsValidBrand(std::string("TES "))); // Ends with white space. + EXPECT_FALSE(IsValidBrand(std::string("T ES"))); // Contains white space. + EXPECT_FALSE(IsValidBrand(std::string("<TE"))); // Has <. + EXPECT_FALSE(IsValidBrand(std::string("TE>"))); // Has >. + EXPECT_FALSE(IsValidBrand(std::string("\""))); // Has " + EXPECT_FALSE(IsValidBrand(std::string("\\"))); // Has backslash. + EXPECT_FALSE(IsValidBrand(std::string("\xaa"))); // Has non-ASCII char. +} + +TEST(UpdateClientUtils, GetCrxComponentId) { + static const uint8_t kHash[16] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + }; + CrxComponent component; + component.pk_hash.assign(kHash, kHash + sizeof(kHash)); + + EXPECT_EQ(std::string("abcdefghijklmnopabcdefghijklmnop"), + GetCrxComponentID(component)); +} + +TEST(UpdateClientUtils, GetCrxIdFromPublicKeyHash) { + static const uint8_t kHash[16] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + }; + + EXPECT_EQ(std::string("abcdefghijklmnopabcdefghijklmnop"), + GetCrxIdFromPublicKeyHash({std::cbegin(kHash), std::cend(kHash)})); +} + +// Tests that the name of an InstallerAttribute matches ^[-_=a-zA-Z0-9]{1,256}$ +TEST(UpdateClientUtils, IsValidInstallerAttributeName) { + // Test the length boundaries. + EXPECT_FALSE(IsValidInstallerAttribute( + make_pair(std::string(0, 'a'), std::string("value")))); + EXPECT_TRUE(IsValidInstallerAttribute( + make_pair(std::string(1, 'a'), std::string("value")))); + EXPECT_TRUE(IsValidInstallerAttribute( + make_pair(std::string(256, 'a'), std::string("value")))); + EXPECT_FALSE(IsValidInstallerAttribute( + make_pair(std::string(257, 'a'), std::string("value")))); + + const char* const valid_names[] = {"A", "Z", "a", "a-b", "A_B", + "z", "0", "9", "-_"}; + for (const char* name : valid_names) + EXPECT_TRUE(IsValidInstallerAttribute( + make_pair(std::string(name), std::string("value")))); + + const char* const invalid_names[] = { + "", "a=1", " name", "name ", "na me", "<name", "name>", + "\"", "\\", "\xaa", ".", ",", ";", "+"}; + for (const char* name : invalid_names) + EXPECT_FALSE(IsValidInstallerAttribute( + make_pair(std::string(name), std::string("value")))); +} + +// Tests that the value of an InstallerAttribute matches +// ^[-.,;+_=a-zA-Z0-9]{0,256}$ +TEST(UpdateClientUtils, IsValidInstallerAttributeValue) { + // Test the length boundaries. + EXPECT_TRUE(IsValidInstallerAttribute( + make_pair(std::string("name"), std::string(0, 'a')))); + EXPECT_TRUE(IsValidInstallerAttribute( + make_pair(std::string("name"), std::string(256, 'a')))); + EXPECT_FALSE(IsValidInstallerAttribute( + make_pair(std::string("name"), std::string(257, 'a')))); + + const char* const valid_values[] = {"", "a=1", "A", "Z", "a", + "z", "0", "9", "-.,;+_="}; + for (const char* value : valid_values) + EXPECT_TRUE(IsValidInstallerAttribute( + make_pair(std::string("name"), std::string(value)))); + + const char* const invalid_values[] = {" ap", "ap ", "a p", "<ap", + "ap>", "\"", "\\", "\xaa"}; + for (const char* value : invalid_values) + EXPECT_FALSE(IsValidInstallerAttribute( + make_pair(std::string("name"), std::string(value)))); +} + +TEST(UpdateClientUtils, RemoveUnsecureUrls) { + const GURL test1[] = {GURL("http://foo"), GURL("https://foo")}; + std::vector<GURL> urls(std::begin(test1), std::end(test1)); + RemoveUnsecureUrls(&urls); + EXPECT_EQ(1u, urls.size()); + EXPECT_EQ(urls[0], GURL("https://foo")); + + const GURL test2[] = {GURL("https://foo"), GURL("http://foo")}; + urls.assign(std::begin(test2), std::end(test2)); + RemoveUnsecureUrls(&urls); + EXPECT_EQ(1u, urls.size()); + EXPECT_EQ(urls[0], GURL("https://foo")); + + const GURL test3[] = {GURL("https://foo"), GURL("https://bar")}; + urls.assign(std::begin(test3), std::end(test3)); + RemoveUnsecureUrls(&urls); + EXPECT_EQ(2u, urls.size()); + EXPECT_EQ(urls[0], GURL("https://foo")); + EXPECT_EQ(urls[1], GURL("https://bar")); + + const GURL test4[] = {GURL("http://foo")}; + urls.assign(std::begin(test4), std::end(test4)); + RemoveUnsecureUrls(&urls); + EXPECT_EQ(0u, urls.size()); + + const GURL test5[] = {GURL("http://foo"), GURL("http://bar")}; + urls.assign(std::begin(test5), std::end(test5)); + RemoveUnsecureUrls(&urls); + EXPECT_EQ(0u, urls.size()); +} + +TEST(UpdateClientUtils, ToInstallerResult) { + enum EnumA { + ENTRY0 = 10, + ENTRY1 = 20, + }; + + enum class EnumB { + ENTRY0 = 0, + ENTRY1, + }; + + const auto result1 = ToInstallerResult(EnumA::ENTRY0); + EXPECT_EQ(110, result1.error); + EXPECT_EQ(0, result1.extended_error); + + const auto result2 = ToInstallerResult(ENTRY1, 10000); + EXPECT_EQ(120, result2.error); + EXPECT_EQ(10000, result2.extended_error); + + const auto result3 = ToInstallerResult(EnumB::ENTRY0); + EXPECT_EQ(100, result3.error); + EXPECT_EQ(0, result3.extended_error); + + const auto result4 = ToInstallerResult(EnumB::ENTRY1, 20000); + EXPECT_EQ(101, result4.error); + EXPECT_EQ(20000, result4.extended_error); +} + +} // namespace update_client
diff --git a/src/v8/gypfiles/toolchain.gypi b/src/v8/gypfiles/toolchain.gypi index 14183a9..a91e8e8 100644 --- a/src/v8/gypfiles/toolchain.gypi +++ b/src/v8/gypfiles/toolchain.gypi
@@ -181,7 +181,7 @@ 'CAN_USE_VFP3_INSTRUCTIONS', ], }], - [ 'arm_fpu=="vfpv3"', { + [ 'arm_fpu=="vfpv3" or arm_fpu=="neon-vfpv4"', { 'defines': [ 'CAN_USE_VFP3_INSTRUCTIONS', 'CAN_USE_VFP32DREGS',
diff --git a/src/v8/src/arm/cpu-arm.cc b/src/v8/src/arm/cpu-arm.cc index f5d2ab1..ecece7d 100644 --- a/src/v8/src/arm/cpu-arm.cc +++ b/src/v8/src/arm/cpu-arm.cc
@@ -8,7 +8,7 @@ #include <sys/mman.h> // for cache flushing. #undef MAP_TYPE #else -#include <sys/syscall.h> // for cache flushing. +#define __ARM_NR_cacheflush 0x0f0002 // for cache flushing. #endif #endif
diff --git a/src/v8/src/base/cpu.cc b/src/v8/src/base/cpu.cc index 47184b9..1ab7b08 100644 --- a/src/v8/src/base/cpu.cc +++ b/src/v8/src/base/cpu.cc
@@ -6,6 +6,7 @@ #if defined(STARBOARD) #include "starboard/client_porting/poem/stdlib_poem.h" +#include "starboard/cpu_features.h" #endif #if V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64 @@ -308,6 +309,54 @@ #endif // V8_HOST_ARCH_ARM || V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64 +#if defined(STARBOARD) + +bool CPU::StarboardDetectCPU() { +#if (SB_API_VERSION >= 11) + SbCPUFeatures features; + if (!SbCPUFeaturesGet(&features)) { + return false; + } + architecture_ = features.arm.architecture_generation; + switch (features.architecture) { + case kSbCPUFeaturesArchitectureArm: + case kSbCPUFeaturesArchitectureArm64: + has_neon_ = features.arm.has_neon; + has_thumb2_ = features.arm.has_thumb2; + has_vfp_ = features.arm.has_vfp; + has_vfp3_ = features.arm.has_vfp3; + has_vfp3_d32_ = features.arm.has_vfp3_d32; + has_idiva_ = features.arm.has_idiva; + break; + case kSbCPUFeaturesArchitectureX86: + case kSbCPUFeaturesArchitectureX86_64: + // Following flags are mandatory for V8 + has_cmov_ = features.x86.has_cmov; + has_sse2_ = features.x86.has_sse2; + // These flags are optional + has_sse3_ = features.x86.has_sse3; + has_ssse3_ = features.x86.has_ssse3; + has_sse41_ = features.x86.has_sse41; + has_sahf_ = features.x86.has_sahf; + has_avx_ = features.x86.has_avx; + has_fma3_ = features.x86.has_fma3; + has_bmi1_ = features.x86.has_bmi1; + has_bmi2_ = features.x86.has_bmi2; + has_lzcnt_ = features.x86.has_lzcnt; + has_popcnt_ = features.x86.has_popcnt; + break; + default: + return false; + } + + return true; +#else // SB_API_VERSION >= 11 + return false; +#endif +} + +#endif + CPU::CPU() : stepping_(0), model_(0), @@ -348,6 +397,13 @@ is_fp64_mode_(false), has_non_stop_time_stamp_counter_(false) { memcpy(vendor_, "Unknown", 8); + +#if defined(STARBOARD) + if (StarboardDetectCPU()) { + return; + } +#endif + #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64 int cpu_info[4];
diff --git a/src/v8/src/base/cpu.h b/src/v8/src/base/cpu.h index 4b4becf..9ef56d3 100644 --- a/src/v8/src/base/cpu.h +++ b/src/v8/src/base/cpu.h
@@ -156,6 +156,9 @@ bool is_fp64_mode_; bool has_non_stop_time_stamp_counter_; bool has_msa_; +#if defined(STARBOARD) + bool StarboardDetectCPU(); +#endif }; } // namespace base