Implement container-progress() function
As introduced in https://drafts.csswg.org/css-values-5/#container-progress-func
The container-progress() functional notation returns a <number> value
representing current value of the size-feature of some container, that
can be either named or unnamed, as a progress value between two explicit
calc values.
Note: for now we only support width and height features.
Note: now it's an editor's draft with a very strong chances to be accepted.
Bug: 40944203
Change-Id: I4664ad8d01174bfdc9621ca304caecc9db37c9ca
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5391308
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Daniil Sakhapov <sakhapov@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1279994}
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc
index 971966d..f4b031e 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -151,6 +151,22 @@
}
}
+CSSMathOperator CSSValueIDToCSSMathOperator(CSSValueID id) {
+ switch (id) {
+#define CONVERSION_CASE(value_id) \
+ case CSSValueID::value_id: \
+ return CSSMathOperator::value_id;
+
+ CONVERSION_CASE(kProgress)
+ CONVERSION_CASE(kMediaProgress)
+ CONVERSION_CASE(kContainerProgress)
+
+#undef CONVERSION_CASE
+ default:
+ NOTREACHED_NORETURN();
+ }
+}
+
static bool HasDoubleValue(CSSPrimitiveValue::UnitType type) {
switch (type) {
case CSSPrimitiveValue::UnitType::kNumber:
@@ -270,6 +286,12 @@
return id == CSSValueID::kWidth || id == CSSValueID::kHeight;
}
+// TODO(crbug.com/40944203): For now we only support width and height
+// size features.
+bool IsAllowedContainerFeature(const CSSValueID& id) {
+ return id == CSSValueID::kWidth || id == CSSValueID::kHeight;
+}
+
bool CheckProgressFunctionTypes(
CSSValueID function_id,
const CSSMathExpressionOperation::Operands& nodes) {
@@ -296,6 +318,17 @@
}
break;
}
+ case CSSValueID::kContainerProgress: {
+ if (!IsAllowedContainerFeature(
+ To<CSSMathExpressionContainerFeature>(*nodes[0]).GetValue())) {
+ return false;
+ }
+ if (nodes[1]->Category() != CalculationResultCategory::kCalcLength ||
+ nodes[2]->Category() != CalculationResultCategory::kCalcLength) {
+ return false;
+ }
+ break;
+ }
default:
NOTREACHED();
break;
@@ -1599,6 +1632,12 @@
return operation;
}
+inline bool CanArithmeticOperationBeSimplified(
+ const CSSMathExpressionNode* left_side,
+ const CSSMathExpressionNode* right_side) {
+ return !left_side->IsOperation() && !right_side->IsOperation();
+}
+
// static
CSSMathExpressionNode*
CSSMathExpressionOperation::CreateArithmeticOperationSimplified(
@@ -1613,7 +1652,7 @@
return result;
}
- if (left_side->IsOperation() || right_side->IsOperation()) {
+ if (!CanArithmeticOperationBeSimplified(left_side, right_side)) {
return CreateArithmeticOperation(left_side, right_side, op);
}
@@ -1933,6 +1972,7 @@
case CSSMathOperator::kSign:
case CSSMathOperator::kProgress:
case CSSMathOperator::kMediaProgress:
+ case CSSMathOperator::kContainerProgress:
return std::nullopt;
case CSSMathOperator::kInvalid:
NOTREACHED();
@@ -2006,6 +2046,7 @@
case CSSMathOperator::kSign:
case CSSMathOperator::kProgress:
case CSSMathOperator::kMediaProgress:
+ case CSSMathOperator::kContainerProgress:
case CSSMathOperator::kCalcSize: {
Vector<scoped_refptr<const CalculationExpressionNode>> operands;
operands.reserve(operands_.size());
@@ -2035,6 +2076,8 @@
op = CalculationOperator::kProgress;
} else if (operator_ == CSSMathOperator::kMediaProgress) {
op = CalculationOperator::kMediaProgress;
+ } else if (operator_ == CSSMathOperator::kContainerProgress) {
+ op = CalculationOperator::kContainerProgress;
} else {
CHECK(operator_ == CSSMathOperator::kCalcSize);
op = CalculationOperator::kCalcSize;
@@ -2158,6 +2201,7 @@
case CSSMathOperator::kProgress:
case CSSMathOperator::kCalcSize:
case CSSMathOperator::kMediaProgress:
+ case CSSMathOperator::kContainerProgress:
return false;
case CSSMathOperator::kInvalid:
NOTREACHED();
@@ -2279,7 +2323,8 @@
return result.ReleaseString();
}
case CSSMathOperator::kProgress:
- case CSSMathOperator::kMediaProgress: {
+ case CSSMathOperator::kMediaProgress:
+ case CSSMathOperator::kContainerProgress: {
CHECK_EQ(operands_.size(), 3u);
StringBuilder result;
result.Append(ToString(operator_));
@@ -2374,6 +2419,7 @@
case CSSMathOperator::kSign:
case CSSMathOperator::kProgress:
case CSSMathOperator::kMediaProgress:
+ case CSSMathOperator::kContainerProgress:
return CSSPrimitiveValue::UnitType::kNumber;
case CSSMathOperator::kCalcSize: {
DCHECK_EQ(operands_.size(), 2u);
@@ -2507,7 +2553,8 @@
return signum;
}
case CSSMathOperator::kProgress:
- case CSSMathOperator::kMediaProgress: {
+ case CSSMathOperator::kMediaProgress:
+ case CSSMathOperator::kContainerProgress: {
CHECK_EQ(operands.size(), 3u);
return (operands[0] - operands[1]) / (operands[2] - operands[1]);
}
@@ -2569,6 +2616,81 @@
// ------ End of CSSMathExpressionOperation member functions ------
+// ------ Start of CSSMathExpressionContainerProgress member functions ----
+
+namespace {
+
+double EvaluateContainerSize(const CSSIdentifierValue* size_feature,
+ const CSSCustomIdentValue* container_name,
+ const CSSLengthResolver& length_resolver) {
+ if (container_name) {
+ ScopedCSSName* name = MakeGarbageCollected<ScopedCSSName>(
+ container_name->Value(), container_name->GetTreeScope());
+ switch (size_feature->GetValueID()) {
+ case CSSValueID::kWidth:
+ return length_resolver.ContainerWidth(*name);
+ case CSSValueID::kHeight:
+ return length_resolver.ContainerHeight(*name);
+ default:
+ NOTREACHED_NORETURN();
+ }
+ } else {
+ switch (size_feature->GetValueID()) {
+ case CSSValueID::kWidth:
+ return length_resolver.ContainerWidth();
+ case CSSValueID::kHeight:
+ return length_resolver.ContainerHeight();
+ default:
+ NOTREACHED_NORETURN();
+ }
+ }
+}
+
+} // namespace
+
+CSSMathExpressionContainerFeature::CSSMathExpressionContainerFeature(
+ const CSSIdentifierValue* size_feature,
+ const CSSCustomIdentValue* container_name)
+ : CSSMathExpressionNode(CalculationResultCategory::kCalcLength,
+ /*has_comparisons =*/false,
+ /*needs_tree_scope_population =*/true),
+ size_feature_(size_feature),
+ container_name_(container_name) {
+ CHECK(size_feature);
+}
+
+String CSSMathExpressionContainerFeature::CustomCSSText() const {
+ StringBuilder builder;
+ builder.Append(size_feature_->CustomCSSText());
+ if (container_name_ && !container_name_->Value().empty()) {
+ builder.Append(" of ");
+ builder.Append(container_name_->CustomCSSText());
+ }
+ return builder.ToString();
+}
+
+scoped_refptr<const CalculationExpressionNode>
+CSSMathExpressionContainerFeature::ToCalculationExpression(
+ const CSSLengthResolver& length_resolver) const {
+ double progress =
+ EvaluateContainerSize(size_feature_, container_name_, length_resolver);
+ return base::MakeRefCounted<CalculationExpressionPixelsAndPercentNode>(
+ PixelsAndPercent(progress));
+}
+
+std::optional<PixelsAndPercent>
+CSSMathExpressionContainerFeature::ToPixelsAndPercent(
+ const CSSLengthResolver& length_resolver) const {
+ return PixelsAndPercent(ComputeDouble(length_resolver));
+}
+
+double CSSMathExpressionContainerFeature::ComputeDouble(
+ const CSSLengthResolver& length_resolver) const {
+ return EvaluateContainerSize(size_feature_, container_name_, length_resolver);
+}
+
+// ------ End of CSSMathExpressionContainerProgress member functions ------
+
// ------ Start of CSSMathExpressionAnchorQuery member functions ------
namespace {
@@ -2979,6 +3101,7 @@
return RuntimeEnabledFeatures::CSSAnchorPositioningEnabled();
case CSSValueID::kProgress:
case CSSValueID::kMediaProgress:
+ case CSSValueID::kContainerProgress:
return RuntimeEnabledFeatures::CSSProgressNotationEnabled();
case CSSValueID::kCalcSize:
return RuntimeEnabledFeatures::CSSCalcSizeFunctionEnabled();
@@ -3080,11 +3203,13 @@
// https://drafts.csswg.org/css-values-5/#progress-func
// https://drafts.csswg.org/css-values-5/#media-progress-func
+ // https://drafts.csswg.org/css-values-5/#container-progress-func
CSSMathExpressionNode* ParseProgressNotation(CSSValueID function_id,
CSSParserTokenRange& tokens,
State state) {
if (function_id != CSSValueID::kProgress &&
- function_id != CSSValueID::kMediaProgress) {
+ function_id != CSSValueID::kMediaProgress &&
+ function_id != CSSValueID::kContainerProgress) {
return nullptr;
}
// <media-progress()> = media-progress(<media-feature> from <calc-sum> to
@@ -3096,6 +3221,27 @@
ParseKeywordLiteral(tokens, CSSMathOperator::kMediaProgress)) {
nodes.push_back(node);
}
+ } else if (function_id == CSSValueID::kContainerProgress) {
+ // <container-progress()> = container-progress(<size-feature> [ of
+ // <container-name> ]? from <calc-sum> to <calc-sum>)
+ const CSSIdentifierValue* size_feature =
+ css_parsing_utils::ConsumeIdent(tokens);
+ if (!size_feature) {
+ return nullptr;
+ }
+ if (tokens.Peek().Id() == CSSValueID::kOf) {
+ tokens.ConsumeIncludingWhitespace();
+ const CSSCustomIdentValue* container_name =
+ css_parsing_utils::ConsumeCustomIdent(tokens, context_);
+ if (!container_name) {
+ return nullptr;
+ }
+ nodes.push_back(MakeGarbageCollected<CSSMathExpressionContainerFeature>(
+ size_feature, container_name));
+ } else {
+ nodes.push_back(MakeGarbageCollected<CSSMathExpressionContainerFeature>(
+ size_feature, nullptr));
+ }
} else if (CSSMathExpressionNode* node =
ParseValueExpression(tokens, state)) {
// <progress()> = progress(<calc-sum> from <calc-sum> to <calc-sum>)
@@ -3137,8 +3283,7 @@
}
return MakeGarbageCollected<CSSMathExpressionOperation>(
CalculationResultCategory::kCalcNumber, std::move(nodes),
- function_id == CSSValueID::kProgress ? CSSMathOperator::kProgress
- : CSSMathOperator::kMediaProgress);
+ CSSValueIDToCSSMathOperator(function_id));
}
CSSMathExpressionNode* ParseCalcSize(CSSValueID function_id,
@@ -3813,7 +3958,8 @@
std::move(operands), op);
}
case CalculationOperator::kProgress:
- case CalculationOperator::kMediaProgress: {
+ case CalculationOperator::kMediaProgress:
+ case CalculationOperator::kContainerProgress: {
CHECK_EQ(children.size(), 3u);
CSSMathExpressionOperation::Operands operands;
operands.push_back(Create(*children.front()));
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.h b/third_party/blink/renderer/core/css/css_math_expression_node.h
index 51ff844..8430dcba 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.h
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.h
@@ -38,12 +38,15 @@
#include "base/dcheck_is_on.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/css/css_anchor_query_enums.h"
+#include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
+#include "third_party/blink/renderer/core/css/css_identifier_value.h"
#include "third_party/blink/renderer/core/css/css_length_resolver.h"
#include "third_party/blink/renderer/core/css/css_math_operator.h"
#include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "third_party/blink/renderer/core/css/css_value.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
+#include "third_party/blink/renderer/core/dom/tree_scope.h"
#include "third_party/blink/renderer/core/layout/geometry/axis.h"
#include "third_party/blink/renderer/platform/geometry/calculation_value.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -118,6 +121,7 @@
virtual bool IsAnchorQuery() const { return false; }
virtual bool IsIdentifierLiteral() const { return false; }
virtual bool IsKeywordLiteral() const { return false; }
+ virtual bool IsContainerFeature() const { return false; }
virtual bool IsMathFunction() const { return false; }
@@ -600,7 +604,8 @@
bool IsCalcSize() const { return operator_ == CSSMathOperator::kCalcSize; }
bool IsProgressNotation() const {
return operator_ == CSSMathOperator::kProgress ||
- operator_ == CSSMathOperator::kMediaProgress;
+ operator_ == CSSMathOperator::kMediaProgress ||
+ operator_ == CSSMathOperator::kContainerProgress;
}
// TODO(crbug.com/1284199): Check other math functions too.
@@ -676,6 +681,96 @@
}
};
+class CORE_EXPORT CSSMathExpressionContainerFeature final
+ : public CSSMathExpressionNode {
+ public:
+ CSSMathExpressionContainerFeature(const CSSIdentifierValue* size_feature,
+ const CSSCustomIdentValue* container_name);
+
+ CSSMathExpressionNode* Copy() const final {
+ return MakeGarbageCollected<CSSMathExpressionContainerFeature>(
+ size_feature_, container_name_);
+ }
+
+ bool IsContainerFeature() const final { return true; }
+
+ const CSSMathExpressionNode& PopulateWithTreeScope(
+ const TreeScope* tree_scope) const final {
+ const CSSCustomIdentValue* container_name =
+ container_name_ ? &container_name_->PopulateWithTreeScope(tree_scope)
+ : nullptr;
+ return *MakeGarbageCollected<CSSMathExpressionContainerFeature>(
+ size_feature_, container_name);
+ }
+ const CSSMathExpressionNode* TransformAnchors(
+ LogicalAxis axis,
+ const TryTacticTransform& transform,
+ const WritingDirectionMode& mode) const final {
+ return this;
+ }
+
+ CSSValueID GetValue() const { return size_feature_->GetValueID(); }
+
+ bool IsZero() const final { return false; }
+ String CustomCSSText() const final;
+ scoped_refptr<const CalculationExpressionNode> ToCalculationExpression(
+ const CSSLengthResolver&) const final;
+ std::optional<PixelsAndPercent> ToPixelsAndPercent(
+ const CSSLengthResolver&) const final;
+ double DoubleValue() const final {
+ NOTREACHED();
+ return 0;
+ }
+ std::optional<double> ComputeValueInCanonicalUnit() const final {
+ return std::nullopt;
+ }
+ double ComputeLengthPx(const CSSLengthResolver& length_resolver) const final {
+ NOTREACHED();
+ return 0;
+ }
+ bool AccumulateLengthArray(CSSLengthArray& length_array,
+ double multiplier) const final {
+ return false;
+ }
+ void AccumulateLengthUnitTypes(
+ CSSPrimitiveValue::LengthTypeFlags& types) const final {}
+ bool IsComputationallyIndependent() const final { return true; }
+ bool operator==(const CSSMathExpressionNode& other) const final {
+ auto* other_progress = DynamicTo<CSSMathExpressionContainerFeature>(other);
+ return other_progress &&
+ base::ValuesEquivalent(other_progress->size_feature_,
+ size_feature_) &&
+ base::ValuesEquivalent(other_progress->container_name_,
+ container_name_);
+ }
+ CSSPrimitiveValue::UnitType ResolvedUnitType() const final {
+ return CSSPrimitiveValue::UnitType::kNumber;
+ }
+ void Trace(Visitor* visitor) const final {
+ visitor->Trace(size_feature_);
+ visitor->Trace(container_name_);
+ CSSMathExpressionNode::Trace(visitor);
+ }
+
+#if DCHECK_IS_ON()
+ bool InvolvesPercentageComparisons() const final { return false; }
+#endif
+
+ protected:
+ double ComputeDouble(const CSSLengthResolver& length_resolver) const final;
+
+ private:
+ Member<const CSSIdentifierValue> size_feature_;
+ Member<const CSSCustomIdentValue> container_name_;
+};
+
+template <>
+struct DowncastTraits<CSSMathExpressionContainerFeature> {
+ static bool AllowFrom(const CSSMathExpressionNode& node) {
+ return node.IsContainerFeature();
+ }
+};
+
// anchor() and anchor-size()
class CORE_EXPORT CSSMathExpressionAnchorQuery final
: public CSSMathExpressionNode {
diff --git a/third_party/blink/renderer/core/css/css_math_operator.cc b/third_party/blink/renderer/core/css/css_math_operator.cc
index e7d841d..c375466 100644
--- a/third_party/blink/renderer/core/css/css_math_operator.cc
+++ b/third_party/blink/renderer/core/css/css_math_operator.cc
@@ -67,6 +67,8 @@
return "calc-size";
case CSSMathOperator::kMediaProgress:
return "media-progress";
+ case CSSMathOperator::kContainerProgress:
+ return "container-progress";
default:
NOTREACHED();
return String();
diff --git a/third_party/blink/renderer/core/css/css_math_operator.h b/third_party/blink/renderer/core/css/css_math_operator.h
index cbb820c..a5b9c6d 100644
--- a/third_party/blink/renderer/core/css/css_math_operator.h
+++ b/third_party/blink/renderer/core/css/css_math_operator.h
@@ -33,6 +33,7 @@
kProgress,
kCalcSize,
kMediaProgress,
+ kContainerProgress,
kInvalid
};
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5
index 3bd8ff9d2..35daa46 100644
--- a/third_party/blink/renderer/core/css/css_value_keywords.json5
+++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -1875,5 +1875,9 @@
// media-progress()
"media-progress",
+
+ // container-progress()
+ "container-progress",
+ "of",
],
}
diff --git a/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc b/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
index 20d9460..cdbed37b 100644
--- a/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
+++ b/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
@@ -326,7 +326,8 @@
}
}
case CalculationOperator::kProgress:
- case CalculationOperator::kMediaProgress: {
+ case CalculationOperator::kMediaProgress:
+ case CalculationOperator::kContainerProgress: {
DCHECK_EQ(children.size(), 3u);
Vector<float, 3> operand_pixels;
bool can_simplify = true;
@@ -490,7 +491,8 @@
return children_[1]->Evaluate(max_value, calculation_input);
}
case CalculationOperator::kProgress:
- case CalculationOperator::kMediaProgress: {
+ case CalculationOperator::kMediaProgress:
+ case CalculationOperator::kContainerProgress: {
DCHECK(!children_.empty());
float progress = children_[0]->Evaluate(max_value, input);
float from = children_[1]->Evaluate(max_value, input);
@@ -553,7 +555,8 @@
case CalculationOperator::kAbs:
case CalculationOperator::kSign:
case CalculationOperator::kProgress:
- case CalculationOperator::kMediaProgress: {
+ case CalculationOperator::kMediaProgress:
+ case CalculationOperator::kContainerProgress: {
DCHECK(children_.size());
Vector<scoped_refptr<const CalculationExpressionNode>> cloned_operands;
cloned_operands.reserve(children_.size());
@@ -631,6 +634,7 @@
return first_child_type;
}
case CalculationOperator::kSign:
+ case CalculationOperator::kContainerProgress:
case CalculationOperator::kProgress:
case CalculationOperator::kMediaProgress:
return ResultType::kNumber;
diff --git a/third_party/blink/renderer/platform/geometry/calculation_expression_node.h b/third_party/blink/renderer/platform/geometry/calculation_expression_node.h
index 328ddec..6b3d694 100644
--- a/third_party/blink/renderer/platform/geometry/calculation_expression_node.h
+++ b/third_party/blink/renderer/platform/geometry/calculation_expression_node.h
@@ -31,6 +31,7 @@
kAbs,
kSign,
kProgress,
+ kContainerProgress,
kCalcSize,
kMediaProgress,
kInvalid
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/container-progress-computed.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-values/container-progress-computed.tentative.html
new file mode 100644
index 0000000..9ab537c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/container-progress-computed.tentative.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#container-progress-func">
+<link rel="author" title="sakhapov@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/numeric-testcommon.js"></script>
+<div id="out-of-scope-container"></div>
+<div id="extra-container">
+ <div id="outer-container">
+ <div id="inner-container">
+ <div id=target></div>
+ </div>
+ </div>
+</div>
+<style>
+:root {
+ font-size: 10px;
+}
+#out-of-scope-container {
+ container: my-container-3 / size;
+ width: 1px;
+ height: 1px;
+}
+#extra-container {
+ container: my-container-2 / size;
+ width: 5051px;
+ height: 1337px;
+}
+#outer-container {
+ container: my-container / size;
+ width: 322px;
+ height: 228px;
+}
+#inner-container {
+ container-type: size;
+ width: 228px;
+ height: 322px;
+}
+#target {
+ font-size: 10px;
+}
+</style>
+<script>
+
+let width = window.innerWidth;
+let height = window.innerHeight;
+
+let extraWidth = 5051;
+let extraHeight = 1337;
+let innerWidth = 228;
+let innerHeight = 322;
+let outerWidth = 322;
+let outerHeight = 228;
+
+// Identity tests
+test_math_used('container-progress(height from 0px to 1px)', innerHeight, {type:'number'});
+test_math_used('container-progress(width of my-container from 0px to 1px)', outerWidth, {type:'number'});
+
+// Nestings
+test_math_used('container-progress(height from container-progress(height from 0px to 1px) * 1px to container-progress(height from 0px to 1px) * 1px)', '0', {type:'number'});
+test_math_used('container-progress(height from container-progress(height from 0px to 1px) * 0.5px to container-progress(height from 0px to 1px) * 1px)', '1', {type:'number'});
+test_math_used('container-progress(height from container-progress(width of my-container from 0px to 1px) * 1px to container-progress(height of my-container-2 from 0px to 1px) * 1px)', (innerHeight - outerWidth) / (extraHeight - outerWidth), {type:'number'});
+
+// General calculations
+test_math_used('calc(container-progress(width from 0px to 50px) * 10px + 100px)', (innerWidth / 50 * 10 + 100) + 'px');
+test_math_used('calc(container-progress(height from 10px to sign(50px - 500em) * 10px))', (innerHeight - 10) / (-10 - 10), {type:'number'});
+test_math_used('calc(container-progress(width of my-container from 0px to 50px) * 10px + 100px)', (outerWidth / 50 * 10 + 100) + 'px');
+test_math_used('calc(container-progress(height of my-container from 10px to sign(50px - 500em) * 10px))', (outerHeight - 10) / (-10 - 10), {type:'number'});
+
+// Fallback
+test_math_used('container-progress(width of non-existing-container from 0px to 1px)', width, {type:'number'});
+test_math_used('container-progress(height of non-existing-container from 0px to 1px)', height, {type:'number'});
+test_math_used('container-progress(width of out-of-scope-container from 0px to 1px)', width, {type:'number'});
+test_math_used('container-progress(height of out-of-scope-container from 0px to 1px)', height, {type:'number'});
+
+// Type checking
+test_math_used('calc(container-progress(width from 0px to 1px) * 1px)', innerWidth + 'px');
+test_math_used('calc(container-progress(height of my-container from 0px to 1px) * 1s)', outerHeight + 's', {type:'time'});
+test_math_used('calc(container-progress(width of my-container-2 from 0px to 1px) * 1deg)', extraWidth + 'deg', {type:'angle', approx:0.001});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/container-progress-invalid.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-values/container-progress-invalid.tentative.html
new file mode 100644
index 0000000..a78fd344
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/container-progress-invalid.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#container-progress-func">
+<link rel="author" title="sakhapov@chromuim.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/parsing-testcommon.js"></script>
+<script>
+function test_invalid_number(value) {
+ test_invalid_value('opacity', value);
+}
+function test_invalid_length(value) {
+ // 'letter-spacing' accepts <length> only, not <percentage> or any mixes.
+ test_invalid_value('letter-spacing', value);
+}
+
+// Syntax checking
+test_invalid_number('container-progress()');
+test_invalid_number('container-progress( )');
+test_invalid_number('container-progress(,)');
+test_invalid_number('container-progress(1 from )');
+test_invalid_number('container-progress(1)');
+test_invalid_number('container-progress(50% to 0)');
+test_invalid_number('container-progress(0 from 1 to)');
+test_invalid_number('container-progress(from to)');
+test_invalid_number('container-progress(from 1 to 0)');
+test_invalid_number('container-progress(3 of 2 from 1 to 0)');
+test_invalid_number('container-progress(width of 2 from 1 to 0)');
+test_invalid_number('container-progress(from 1 to 0 1)');
+test_invalid_number('container-progress(from 1 0)');
+test_invalid_number('container-progress(0 from to 0)');
+test_invalid_number('container-progress(to to to to to)');
+test_invalid_number('container-progress(0, from, 10, to 200)');
+test_invalid_number('container-progress(0, from, 10, to, 200)');
+test_invalid_number('container-progress(0, from 10, to 200)');
+test_invalid_number('container-progress(0, 10, 200)');
+
+// General tests
+test_invalid_number('container-progress(height from 0 to 8');
+test_invalid_number('container-progress(height container from 0 to 8');
+test_invalid_number('container-progress(height of from 0 to 8');
+test_invalid_number('container-progress(depth from 0px to 8px');
+test_invalid_number('container-progress(width of 10 from 0px to 8px');
+test_invalid_number('container-progress(height of 10 from 0px to 8px');
+test_invalid_number('container-progress(height of name from 0deg to 8deg');
+test_invalid_number('container-progress(height of name from 0 to 8px');
+test_invalid_number('container-progress(10px from 0px to 8px');
+test_invalid_number('container-progress(depth of name from 0px to 8px');
+test_invalid_number('container-progress(width from 0deg to 8deg');
+test_invalid_number('container-progress(5 from 0deg to 8deg');
+test_invalid_number('container-progress(5 from 0% to 8deg');
+test_invalid_number('container-progress(height from 0% to sign(10px)');
+test_invalid_number('container-progress(5% from 0px to 10px');
+test_invalid_length('calc(1px * container-progress(10deg from 0 to 10))');
+test_invalid_length('calc(1px * container-progress(10 from 0px to 10))');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/container-progress-serialize.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-values/container-progress-serialize.tentative.html
new file mode 100644
index 0000000..181054c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/container-progress-serialize.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#container-progress-func">
+<link rel="author" title="sakhapov@chromuim.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/serialize-testcommon.js"></script>
+<div id="outer-container">
+ <div id="inner-container">
+ <div id=target></div>
+ </div>
+</div>
+<style>
+:root {
+ font-size: 10px;
+}
+#outer-container {
+ container: my-container / size;
+ width: 322px;
+ height: 228px;
+}
+#inner-container {
+ container-type: size;
+ width: 228px;
+ height: 322px;
+}
+#target {
+ font-size: 10px;
+}
+</style>
+<script>
+function test_serialization(t,s,c) {
+ test_specified_serialization('opacity', t, s);
+ test_specified_serialization('transform', `scale(${t})`, `scale(${s})`);
+ test_computed_serialization('opacity', t, c);
+ test_computed_serialization('transform', `scale(${t})`, `matrix(${c}, 0, 0, ${c}, 0, 0)`);
+}
+
+test_serialization(
+ 'calc(container-progress(width from 0px to 1px) / 1000)',
+ 'calc(container-progress(width from 0px to 1px) / 1000)',
+ '0.228',
+);
+test_serialization(
+ 'calc(0.1 * container-progress(height of my-container from 0px to 10em))',
+ 'calc(0.1 * container-progress(height of my-container from 0px to 10em))',
+ '0.228',
+);
+</script>