blob: e7c767f2c26b786919b1590468ce648156e6f32b [file] [log] [blame]
// 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/computed_style_property_map.h"
#include "third_party/blink/renderer/core/css/computed_style_css_value_mapping.h"
#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
#include "third_party/blink/renderer/core/css/css_function_value.h"
#include "third_party/blink/renderer/core/css/css_identifier_value.h"
#include "third_party/blink/renderer/core/css/css_variable_data.h"
#include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/pseudo_element.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
#include "third_party/blink/renderer/platform/transforms/matrix_transform_operation.h"
#include "third_party/blink/renderer/platform/transforms/perspective_transform_operation.h"
#include "third_party/blink/renderer/platform/transforms/skew_transform_operation.h"
namespace blink {
namespace {
// We collapse functions like translateX into translate, since we will reify
// them as a translate anyway.
const CSSValue* ComputedTransformComponent(const TransformOperation& operation,
float zoom) {
switch (operation.GetType()) {
case TransformOperation::kScaleX:
case TransformOperation::kScaleY:
case TransformOperation::kScaleZ:
case TransformOperation::kScale:
case TransformOperation::kScale3D: {
const auto& scale = ToScaleTransformOperation(operation);
CSSFunctionValue* result = CSSFunctionValue::Create(
operation.Is3DOperation() ? CSSValueScale3d : CSSValueScale);
result->Append(*CSSPrimitiveValue::Create(
scale.X(), CSSPrimitiveValue::UnitType::kNumber));
result->Append(*CSSPrimitiveValue::Create(
scale.Y(), CSSPrimitiveValue::UnitType::kNumber));
if (operation.Is3DOperation()) {
result->Append(*CSSPrimitiveValue::Create(
scale.Z(), CSSPrimitiveValue::UnitType::kNumber));
}
return result;
}
case TransformOperation::kTranslateX:
case TransformOperation::kTranslateY:
case TransformOperation::kTranslateZ:
case TransformOperation::kTranslate:
case TransformOperation::kTranslate3D: {
const auto& translate = ToTranslateTransformOperation(operation);
CSSFunctionValue* result = CSSFunctionValue::Create(
operation.Is3DOperation() ? CSSValueTranslate3d : CSSValueTranslate);
result->Append(*CSSPrimitiveValue::Create(translate.X(), zoom));
result->Append(*CSSPrimitiveValue::Create(translate.Y(), zoom));
if (operation.Is3DOperation()) {
result->Append(*CSSPrimitiveValue::Create(
translate.Z(), CSSPrimitiveValue::UnitType::kPixels));
}
return result;
}
case TransformOperation::kRotateX:
case TransformOperation::kRotateY:
case TransformOperation::kRotate3D: {
const auto& rotate = ToRotateTransformOperation(operation);
CSSFunctionValue* result = CSSFunctionValue::Create(CSSValueRotate3d);
result->Append(*CSSPrimitiveValue::Create(
rotate.X(), CSSPrimitiveValue::UnitType::kNumber));
result->Append(*CSSPrimitiveValue::Create(
rotate.Y(), CSSPrimitiveValue::UnitType::kNumber));
result->Append(*CSSPrimitiveValue::Create(
rotate.Z(), CSSPrimitiveValue::UnitType::kNumber));
result->Append(*CSSPrimitiveValue::Create(
rotate.Angle(), CSSPrimitiveValue::UnitType::kDegrees));
return result;
}
case TransformOperation::kRotate: {
const auto& rotate = ToRotateTransformOperation(operation);
CSSFunctionValue* result = CSSFunctionValue::Create(CSSValueRotate);
result->Append(*CSSPrimitiveValue::Create(
rotate.Angle(), CSSPrimitiveValue::UnitType::kDegrees));
return result;
}
case TransformOperation::kSkewX: {
const auto& skew = ToSkewTransformOperation(operation);
CSSFunctionValue* result = CSSFunctionValue::Create(CSSValueSkewX);
result->Append(*CSSPrimitiveValue::Create(
skew.AngleX(), CSSPrimitiveValue::UnitType::kDegrees));
return result;
}
case TransformOperation::kSkewY: {
const auto& skew = ToSkewTransformOperation(operation);
CSSFunctionValue* result = CSSFunctionValue::Create(CSSValueSkewY);
result->Append(*CSSPrimitiveValue::Create(
skew.AngleY(), CSSPrimitiveValue::UnitType::kDegrees));
return result;
}
case TransformOperation::kSkew: {
const auto& skew = ToSkewTransformOperation(operation);
CSSFunctionValue* result = CSSFunctionValue::Create(CSSValueSkew);
result->Append(*CSSPrimitiveValue::Create(
skew.AngleX(), CSSPrimitiveValue::UnitType::kDegrees));
result->Append(*CSSPrimitiveValue::Create(
skew.AngleY(), CSSPrimitiveValue::UnitType::kDegrees));
return result;
}
case TransformOperation::kPerspective: {
const auto& perspective = ToPerspectiveTransformOperation(operation);
CSSFunctionValue* result = CSSFunctionValue::Create(CSSValuePerspective);
result->Append(*CSSPrimitiveValue::Create(
perspective.Perspective(), CSSPrimitiveValue::UnitType::kPixels));
return result;
}
case TransformOperation::kMatrix: {
const auto& matrix = ToMatrixTransformOperation(operation).Matrix();
CSSFunctionValue* result = CSSFunctionValue::Create(CSSValueMatrix);
double values[6] = {matrix.A(), matrix.B(), matrix.C(),
matrix.D(), matrix.E(), matrix.F()};
for (double value : values) {
result->Append(*CSSPrimitiveValue::Create(
value, CSSPrimitiveValue::UnitType::kNumber));
}
return result;
}
case TransformOperation::kMatrix3D: {
const auto& matrix = ToMatrix3DTransformOperation(operation).Matrix();
CSSFunctionValue* result = CSSFunctionValue::Create(CSSValueMatrix3d);
double values[16] = {
matrix.M11(), matrix.M12(), matrix.M13(), matrix.M14(),
matrix.M21(), matrix.M22(), matrix.M23(), matrix.M24(),
matrix.M31(), matrix.M32(), matrix.M33(), matrix.M34(),
matrix.M41(), matrix.M42(), matrix.M43(), matrix.M44()};
for (double value : values) {
result->Append(*CSSPrimitiveValue::Create(
value, CSSPrimitiveValue::UnitType::kNumber));
}
return result;
}
case TransformOperation::kInterpolated:
// TODO(816803): The computed value in this case is not fully spec'd
// See https://github.com/w3c/css-houdini-drafts/issues/425
return CSSIdentifierValue::Create(CSSValueNone);
default:
// The remaining operations are unsupported.
NOTREACHED();
return CSSIdentifierValue::Create(CSSValueNone);
}
}
const CSSValue* ComputedTransform(const ComputedStyle& style) {
if (style.Transform().Operations().size() == 0)
return CSSIdentifierValue::Create(CSSValueNone);
CSSValueList* components = CSSValueList::CreateSpaceSeparated();
for (const auto& operation : style.Transform().Operations()) {
components->Append(
*ComputedTransformComponent(*operation, style.EffectiveZoom()));
}
return components;
}
} // namespace
unsigned int ComputedStylePropertyMap::size() {
const ComputedStyle* style = UpdateStyle();
if (!style)
return 0;
DCHECK(StyledNode());
return CSSComputedStyleDeclaration::ComputableProperties().size() +
ComputedStyleCSSValueMapping::GetVariables(
*style, StyledNode()->GetDocument().GetPropertyRegistry())
.size();
}
bool ComputedStylePropertyMap::ComparePropertyNames(const String& a,
const String& b) {
if (a.StartsWith("--"))
return b.StartsWith("--") && WTF::CodePointCompareLessThan(a, b);
if (a.StartsWith("-")) {
return b.StartsWith("--") ||
(b.StartsWith("-") && WTF::CodePointCompareLessThan(a, b));
}
return b.StartsWith("-") || WTF::CodePointCompareLessThan(a, b);
}
Node* ComputedStylePropertyMap::StyledNode() const {
DCHECK(node_);
if (!pseudo_id_)
return node_;
if (node_->IsElementNode()) {
if (PseudoElement* element =
(ToElement(node_))->GetPseudoElement(pseudo_id_)) {
return element;
}
}
return nullptr;
}
const ComputedStyle* ComputedStylePropertyMap::UpdateStyle() {
Node* node = StyledNode();
if (!node || !node->InActiveDocument())
return nullptr;
// Update style before getting the value for the property
// This could cause the node to be blown away. This code is copied from
// CSSComputedStyleDeclaration::GetPropertyCSSValue.
node->GetDocument().UpdateStyleAndLayoutTreeForNode(node);
node = StyledNode();
if (!node)
return nullptr;
// This is copied from CSSComputedStyleDeclaration::computeComputedStyle().
// PseudoIdNone must be used if node() is a PseudoElement.
const ComputedStyle* style = node->EnsureComputedStyle(
node->IsPseudoElement() ? kPseudoIdNone : pseudo_id_);
node = StyledNode();
if (!node || !node->InActiveDocument() || !style)
return nullptr;
return style;
}
const CSSValue* ComputedStylePropertyMap::GetProperty(
CSSPropertyID property_id) {
const ComputedStyle* style = UpdateStyle();
if (!style)
return nullptr;
// Special cases for properties where CSSProperty::CSSValueFromComputedStyle
// doesn't return the correct computed value
switch (property_id) {
case CSSPropertyTransform:
return ComputedTransform(*style);
default:
return CSSProperty::Get(property_id)
.CSSValueFromComputedStyle(*style, nullptr /* layout_object */,
StyledNode(),
false /* allow_visited_style */);
}
}
const CSSValue* ComputedStylePropertyMap::GetCustomProperty(
AtomicString property_name) {
const ComputedStyle* style = UpdateStyle();
if (!style)
return nullptr;
CSSPropertyRef ref(property_name, node_->GetDocument());
return ref.GetProperty().CSSValueFromComputedStyle(
*style, nullptr /* layout_object */, StyledNode(),
false /* allow_visited_style */);
}
void ComputedStylePropertyMap::ForEachProperty(
const IterationCallback& callback) {
const ComputedStyle* style = UpdateStyle();
if (!style)
return;
// Have to sort by all properties by code point, so we have to store
// them in a buffer first.
HeapVector<std::pair<AtomicString, Member<const CSSValue>>> values;
for (const CSSProperty* property :
CSSComputedStyleDeclaration::ComputableProperties()) {
DCHECK(property);
DCHECK(!property->IDEquals(CSSPropertyVariable));
const CSSValue* value = property->CSSValueFromComputedStyle(
*style, nullptr /* layout_object */, StyledNode(), false);
if (value)
values.emplace_back(property->GetPropertyNameAtomicString(), value);
}
PropertyRegistry* registry =
StyledNode()->GetDocument().GetPropertyRegistry();
for (const auto& name_value :
ComputedStyleCSSValueMapping::GetVariables(*style, registry)) {
values.emplace_back(name_value.key, name_value.value);
}
std::sort(values.begin(), values.end(), [](const auto& a, const auto& b) {
return ComparePropertyNames(a.first, b.first);
});
for (const auto& value : values)
callback(value.first, *value.second);
}
String ComputedStylePropertyMap::SerializationForShorthand(
const CSSProperty& property) {
DCHECK(property.IsShorthand());
const ComputedStyle* style = UpdateStyle();
if (!style) {
NOTREACHED();
return "";
}
if (const CSSValue* value = property.CSSValueFromComputedStyle(
*style, nullptr /* layout_object */, StyledNode(), false)) {
return value->CssText();
}
NOTREACHED();
return "";
}
} // namespace blink