// 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 "third_party/blink/renderer/core/css/cssom/css_scale.h" #include "third_party/blink/renderer/bindings/core/v8/v8_union_cssnumericvalue_double.h" #include "third_party/blink/renderer/core/css/css_math_expression_node.h" #include "third_party/blink/renderer/core/css/css_primitive_value.h" #include "third_party/blink/renderer/core/css/cssom/css_numeric_value.h" #include "third_party/blink/renderer/core/css/cssom/css_style_value.h" namespace blink { namespace { bool IsValidScaleCoord(CSSNumericValue* coord) { // TODO(crbug.com/1188610): Following might be needed for another CSSOM // constructor to resolve the valid type for 'calc'. if (coord && coord->GetType() != CSSStyleValue::StyleValueType::kUnitType) { const CSSMathExpressionNode* node = coord->ToCalcExpressionNode(); if (!node) return false; CSSPrimitiveValue::UnitType resolved_type = node->ResolvedUnitType(); return (resolved_type == CSSPrimitiveValue::UnitType::kNumber || resolved_type == CSSPrimitiveValue::UnitType::kInteger); } return coord && coord->Type().MatchesNumber(); } CSSScale* FromScale(const CSSFunctionValue& value) { DCHECK(value.length() == 1U || value.length() == 2U); CSSNumericValue* x = CSSNumericValue::FromCSSValue(To(value.Item(0))); if (value.length() == 1U) { return CSSScale::Create(x, x); } CSSNumericValue* y = CSSNumericValue::FromCSSValue(To(value.Item(1))); return CSSScale::Create(x, y); } CSSScale* FromScaleXYZ(const CSSFunctionValue& value) { DCHECK_EQ(value.length(), 1U); CSSNumericValue* numeric_value = CSSNumericValue::FromCSSValue(To(value.Item(0))); CSSUnitValue* default_value = CSSUnitValue::Create(1); switch (value.FunctionType()) { case CSSValueID::kScaleX: return CSSScale::Create(numeric_value, default_value); case CSSValueID::kScaleY: return CSSScale::Create(default_value, numeric_value); case CSSValueID::kScaleZ: return CSSScale::Create(default_value, default_value, numeric_value); default: NOTREACHED(); return nullptr; } } CSSScale* FromScale3d(const CSSFunctionValue& value) { DCHECK_EQ(value.length(), 3U); CSSNumericValue* x = CSSNumericValue::FromCSSValue(To(value.Item(0))); CSSNumericValue* y = CSSNumericValue::FromCSSValue(To(value.Item(1))); CSSNumericValue* z = CSSNumericValue::FromCSSValue(To(value.Item(2))); return CSSScale::Create(x, y, z); } } // namespace CSSScale* CSSScale::Create(const V8CSSNumberish* x, const V8CSSNumberish* y, ExceptionState& exception_state) { CSSNumericValue* x_value = CSSNumericValue::FromNumberish(x); CSSNumericValue* y_value = CSSNumericValue::FromNumberish(y); if (!IsValidScaleCoord(x_value) || !IsValidScaleCoord(y_value)) { exception_state.ThrowTypeError("Must specify an number unit"); return nullptr; } return CSSScale::Create(x_value, y_value); } CSSScale* CSSScale::Create(const V8CSSNumberish* x, const V8CSSNumberish* y, const V8CSSNumberish* z, ExceptionState& exception_state) { CSSNumericValue* x_value = CSSNumericValue::FromNumberish(x); CSSNumericValue* y_value = CSSNumericValue::FromNumberish(y); CSSNumericValue* z_value = CSSNumericValue::FromNumberish(z); if (!IsValidScaleCoord(x_value) || !IsValidScaleCoord(y_value) || !IsValidScaleCoord(z_value)) { exception_state.ThrowTypeError("Must specify a number for X, Y and Z"); return nullptr; } return CSSScale::Create(x_value, y_value, z_value); } CSSScale* CSSScale::FromCSSValue(const CSSFunctionValue& value) { switch (value.FunctionType()) { case CSSValueID::kScale: return FromScale(value); case CSSValueID::kScaleX: case CSSValueID::kScaleY: case CSSValueID::kScaleZ: return FromScaleXYZ(value); case CSSValueID::kScale3d: return FromScale3d(value); default: NOTREACHED(); return nullptr; } } V8CSSNumberish* CSSScale::x() { return MakeGarbageCollected(x_); } V8CSSNumberish* CSSScale::y() { return MakeGarbageCollected(y_); } V8CSSNumberish* CSSScale::z() { return MakeGarbageCollected(z_); } void CSSScale::setX(const V8CSSNumberish* x, ExceptionState& exception_state) { CSSNumericValue* value = CSSNumericValue::FromNumberish(x); if (!IsValidScaleCoord(value)) { exception_state.ThrowTypeError("Must specify a number unit"); return; } x_ = value; } void CSSScale::setY(const V8CSSNumberish* y, ExceptionState& exception_state) { CSSNumericValue* value = CSSNumericValue::FromNumberish(y); if (!IsValidScaleCoord(value)) { exception_state.ThrowTypeError("Must specify a number unit"); return; } y_ = value; } void CSSScale::setZ(const V8CSSNumberish* z, ExceptionState& exception_state) { CSSNumericValue* value = CSSNumericValue::FromNumberish(z); if (!IsValidScaleCoord(value)) { exception_state.ThrowTypeError("Must specify a number unit"); return; } z_ = value; } DOMMatrix* CSSScale::toMatrix(ExceptionState& exception_state) const { CSSUnitValue* x = x_->to(CSSPrimitiveValue::UnitType::kNumber); CSSUnitValue* y = y_->to(CSSPrimitiveValue::UnitType::kNumber); CSSUnitValue* z = z_->to(CSSPrimitiveValue::UnitType::kNumber); if (!x || !y || !z) { exception_state.ThrowTypeError( "Cannot create matrix if values are not numbers"); return nullptr; } DOMMatrix* matrix = DOMMatrix::Create(); if (is2D()) matrix->scaleSelf(x->value(), y->value()); else matrix->scaleSelf(x->value(), y->value(), z->value()); return matrix; } const CSSFunctionValue* CSSScale::ToCSSValue() const { const CSSValue* x = x_->ToCSSValue(); const CSSValue* y = y_->ToCSSValue(); if (!x || !y) return nullptr; CSSFunctionValue* result = MakeGarbageCollected( is2D() ? CSSValueID::kScale : CSSValueID::kScale3d); result->Append(*x); result->Append(*y); if (!is2D()) { const CSSValue* z = z_->ToCSSValue(); if (!z) return nullptr; result->Append(*z); } return result; } CSSScale::CSSScale(CSSNumericValue* x, CSSNumericValue* y, CSSNumericValue* z, bool is2D) : CSSTransformComponent(is2D), x_(x), y_(y), z_(z) { DCHECK(IsValidScaleCoord(x)); DCHECK(IsValidScaleCoord(y)); DCHECK(IsValidScaleCoord(z)); } } // namespace blink