mirror of
https://github.com/darlinghq/darling-WebCore.git
synced 2024-11-26 22:00:22 +00:00
434 lines
16 KiB
C++
434 lines
16 KiB
C++
/*
|
|
* Copyright (C) 2019 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS CONTRIBUTORS
|
|
* 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.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "PropertyCascade.h"
|
|
|
|
#include "CSSPaintImageValue.h"
|
|
#include "CSSPrimitiveValueMappings.h"
|
|
#include "CSSValuePool.h"
|
|
#include "PaintWorkletGlobalScope.h"
|
|
#include "StyleBuilderGenerated.h"
|
|
#include "StylePropertyShorthand.h"
|
|
|
|
namespace WebCore {
|
|
namespace Style {
|
|
|
|
static inline bool shouldApplyPropertyInParseOrder(CSSPropertyID propertyID)
|
|
{
|
|
switch (propertyID) {
|
|
case CSSPropertyWebkitBackgroundClip:
|
|
case CSSPropertyBackgroundClip:
|
|
case CSSPropertyWebkitBackgroundOrigin:
|
|
case CSSPropertyBackgroundOrigin:
|
|
case CSSPropertyWebkitBackgroundSize:
|
|
case CSSPropertyBackgroundSize:
|
|
case CSSPropertyWebkitBorderImage:
|
|
case CSSPropertyBorderImage:
|
|
case CSSPropertyBorderImageSlice:
|
|
case CSSPropertyBorderImageSource:
|
|
case CSSPropertyBorderImageOutset:
|
|
case CSSPropertyBorderImageRepeat:
|
|
case CSSPropertyBorderImageWidth:
|
|
case CSSPropertyWebkitBoxShadow:
|
|
case CSSPropertyBoxShadow:
|
|
case CSSPropertyWebkitTextDecoration:
|
|
case CSSPropertyTextDecorationLine:
|
|
case CSSPropertyTextDecorationStyle:
|
|
case CSSPropertyTextDecorationColor:
|
|
case CSSPropertyTextDecorationSkip:
|
|
case CSSPropertyTextUnderlinePosition:
|
|
case CSSPropertyTextUnderlineOffset:
|
|
case CSSPropertyTextDecorationThickness:
|
|
case CSSPropertyTextDecoration:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// https://www.w3.org/TR/css-pseudo-4/#marker-pseudo (Editor's Draft, 25 July 2017)
|
|
// FIXME: this is outdated, see https://bugs.webkit.org/show_bug.cgi?id=218791.
|
|
static inline bool isValidMarkerStyleProperty(CSSPropertyID id)
|
|
{
|
|
switch (id) {
|
|
case CSSPropertyColor:
|
|
case CSSPropertyFontFamily:
|
|
case CSSPropertyFontFeatureSettings:
|
|
case CSSPropertyFontSize:
|
|
case CSSPropertyFontStretch:
|
|
case CSSPropertyFontStyle:
|
|
case CSSPropertyFontSynthesis:
|
|
case CSSPropertyFontVariantAlternates:
|
|
case CSSPropertyFontVariantCaps:
|
|
case CSSPropertyFontVariantEastAsian:
|
|
case CSSPropertyFontVariantLigatures:
|
|
case CSSPropertyFontVariantNumeric:
|
|
case CSSPropertyFontVariantPosition:
|
|
case CSSPropertyFontWeight:
|
|
#if ENABLE(VARIATION_FONTS)
|
|
case CSSPropertyFontOpticalSizing:
|
|
case CSSPropertyFontVariationSettings:
|
|
#endif
|
|
case CSSPropertyAnimationDuration:
|
|
case CSSPropertyAnimationTimingFunction:
|
|
case CSSPropertyAnimationDelay:
|
|
case CSSPropertyAnimationIterationCount:
|
|
case CSSPropertyAnimationDirection:
|
|
case CSSPropertyAnimationFillMode:
|
|
case CSSPropertyAnimationPlayState:
|
|
case CSSPropertyAnimationName:
|
|
case CSSPropertyTransitionDuration:
|
|
case CSSPropertyTransitionTimingFunction:
|
|
case CSSPropertyTransitionDelay:
|
|
case CSSPropertyTransitionProperty:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#if ENABLE(VIDEO)
|
|
static inline bool isValidCueStyleProperty(CSSPropertyID id)
|
|
{
|
|
switch (id) {
|
|
case CSSPropertyBackground:
|
|
case CSSPropertyBackgroundAttachment:
|
|
case CSSPropertyBackgroundClip:
|
|
case CSSPropertyBackgroundColor:
|
|
case CSSPropertyBackgroundImage:
|
|
case CSSPropertyBackgroundOrigin:
|
|
case CSSPropertyBackgroundPosition:
|
|
case CSSPropertyBackgroundPositionX:
|
|
case CSSPropertyBackgroundPositionY:
|
|
case CSSPropertyBackgroundRepeat:
|
|
case CSSPropertyBackgroundSize:
|
|
case CSSPropertyColor:
|
|
case CSSPropertyFont:
|
|
case CSSPropertyFontFamily:
|
|
case CSSPropertyFontSize:
|
|
case CSSPropertyFontStyle:
|
|
case CSSPropertyFontVariantCaps:
|
|
case CSSPropertyFontWeight:
|
|
case CSSPropertyLineHeight:
|
|
case CSSPropertyOpacity:
|
|
case CSSPropertyOutline:
|
|
case CSSPropertyOutlineColor:
|
|
case CSSPropertyOutlineOffset:
|
|
case CSSPropertyOutlineStyle:
|
|
case CSSPropertyOutlineWidth:
|
|
case CSSPropertyVisibility:
|
|
case CSSPropertyWhiteSpace:
|
|
case CSSPropertyTextDecoration:
|
|
case CSSPropertyTextShadow:
|
|
case CSSPropertyBorderStyle:
|
|
case CSSPropertyPaintOrder:
|
|
case CSSPropertyStrokeLinejoin:
|
|
case CSSPropertyStrokeLinecap:
|
|
case CSSPropertyStrokeColor:
|
|
case CSSPropertyStrokeWidth:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
PropertyCascade::PropertyCascade(const MatchResult& matchResult, OptionSet<CascadeLevel> cascadeLevels, IncludedProperties includedProperties, Direction direction)
|
|
: m_matchResult(matchResult)
|
|
, m_includedProperties(includedProperties)
|
|
, m_direction(direction)
|
|
{
|
|
buildCascade(cascadeLevels);
|
|
}
|
|
|
|
PropertyCascade::PropertyCascade(const PropertyCascade& parent, OptionSet<CascadeLevel> cascadeLevels)
|
|
: m_matchResult(parent.m_matchResult)
|
|
, m_includedProperties(parent.m_includedProperties)
|
|
, m_direction(parent.direction())
|
|
, m_directionIsUnresolved(false)
|
|
{
|
|
buildCascade(cascadeLevels);
|
|
}
|
|
|
|
PropertyCascade::~PropertyCascade() = default;
|
|
|
|
void PropertyCascade::buildCascade(OptionSet<CascadeLevel> cascadeLevels)
|
|
{
|
|
OptionSet<CascadeLevel> cascadeLevelsWithImportant;
|
|
|
|
for (auto cascadeLevel : cascadeLevels) {
|
|
bool hasImportant = addNormalMatches(cascadeLevel);
|
|
if (hasImportant)
|
|
cascadeLevelsWithImportant.add(cascadeLevel);
|
|
}
|
|
|
|
for (auto cascadeLevel : { CascadeLevel::Author, CascadeLevel::User, CascadeLevel::UserAgent }) {
|
|
if (!cascadeLevelsWithImportant.contains(cascadeLevel))
|
|
continue;
|
|
addImportantMatches(cascadeLevel);
|
|
}
|
|
}
|
|
|
|
void PropertyCascade::setPropertyInternal(Property& property, CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel, ScopeOrdinal styleScopeOrdinal)
|
|
{
|
|
ASSERT(linkMatchType <= SelectorChecker::MatchAll);
|
|
property.id = id;
|
|
property.level = cascadeLevel;
|
|
property.styleScopeOrdinal = styleScopeOrdinal;
|
|
if (linkMatchType == SelectorChecker::MatchAll) {
|
|
property.cssValue[0] = &cssValue;
|
|
property.cssValue[SelectorChecker::MatchLink] = &cssValue;
|
|
property.cssValue[SelectorChecker::MatchVisited] = &cssValue;
|
|
} else
|
|
property.cssValue[linkMatchType] = &cssValue;
|
|
}
|
|
|
|
void PropertyCascade::set(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel, ScopeOrdinal styleScopeOrdinal)
|
|
{
|
|
if (CSSProperty::isDirectionAwareProperty(id)) {
|
|
auto direction = this->direction();
|
|
id = CSSProperty::resolveDirectionAwareProperty(id, direction.textDirection, direction.writingMode);
|
|
}
|
|
|
|
ASSERT(!shouldApplyPropertyInParseOrder(id));
|
|
|
|
auto& property = m_properties[id];
|
|
ASSERT(id < m_propertyIsPresent.size());
|
|
if (id == CSSPropertyCustom) {
|
|
m_propertyIsPresent.set(id);
|
|
const auto& customValue = downcast<CSSCustomPropertyValue>(cssValue);
|
|
bool hasValue = m_customProperties.contains(customValue.name());
|
|
if (!hasValue) {
|
|
Property property;
|
|
property.id = id;
|
|
memset(property.cssValue, 0, sizeof(property.cssValue));
|
|
setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
|
|
m_customProperties.set(customValue.name(), property);
|
|
} else {
|
|
Property property = customProperty(customValue.name());
|
|
setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
|
|
m_customProperties.set(customValue.name(), property);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!m_propertyIsPresent[id])
|
|
memset(property.cssValue, 0, sizeof(property.cssValue));
|
|
m_propertyIsPresent.set(id);
|
|
setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
|
|
}
|
|
|
|
void PropertyCascade::setDeferred(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel, ScopeOrdinal styleScopeOrdinal)
|
|
{
|
|
ASSERT(!CSSProperty::isDirectionAwareProperty(id));
|
|
ASSERT(shouldApplyPropertyInParseOrder(id));
|
|
|
|
Property property;
|
|
memset(property.cssValue, 0, sizeof(property.cssValue));
|
|
setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
|
|
m_deferredProperties.append(property);
|
|
}
|
|
|
|
|
|
bool PropertyCascade::addMatch(const MatchedProperties& matchedProperties, CascadeLevel cascadeLevel, bool important)
|
|
{
|
|
auto& styleProperties = *matchedProperties.properties;
|
|
auto propertyAllowlistType = static_cast<PropertyAllowlistType>(matchedProperties.allowlistType);
|
|
bool hasImportantProperties = false;
|
|
|
|
for (unsigned i = 0, count = styleProperties.propertyCount(); i < count; ++i) {
|
|
auto current = styleProperties.propertyAt(i);
|
|
|
|
if (current.isImportant())
|
|
hasImportantProperties = true;
|
|
if (important != current.isImportant())
|
|
continue;
|
|
|
|
if (m_includedProperties == IncludedProperties::InheritedOnly && !current.isInherited()) {
|
|
// Inherited only mode is used after matched properties cache hit.
|
|
// A match with a value that is explicitly inherited should never have been cached.
|
|
ASSERT(!current.value()->isInheritedValue());
|
|
continue;
|
|
}
|
|
CSSPropertyID propertyID = current.id();
|
|
|
|
#if ENABLE(VIDEO)
|
|
if (propertyAllowlistType == PropertyAllowlistCue && !isValidCueStyleProperty(propertyID))
|
|
continue;
|
|
#endif
|
|
if (propertyAllowlistType == PropertyAllowlistMarker && !isValidMarkerStyleProperty(propertyID))
|
|
continue;
|
|
|
|
if (shouldApplyPropertyInParseOrder(propertyID))
|
|
setDeferred(propertyID, *current.value(), matchedProperties.linkMatchType, cascadeLevel, matchedProperties.styleScopeOrdinal);
|
|
else
|
|
set(propertyID, *current.value(), matchedProperties.linkMatchType, cascadeLevel, matchedProperties.styleScopeOrdinal);
|
|
}
|
|
|
|
return hasImportantProperties;
|
|
}
|
|
|
|
static auto& declarationsForCascadeLevel(const MatchResult& matchResult, CascadeLevel cascadeLevel)
|
|
{
|
|
switch (cascadeLevel) {
|
|
case CascadeLevel::UserAgent: return matchResult.userAgentDeclarations;
|
|
case CascadeLevel::User: return matchResult.userDeclarations;
|
|
case CascadeLevel::Author: return matchResult.authorDeclarations;
|
|
}
|
|
ASSERT_NOT_REACHED();
|
|
return matchResult.authorDeclarations;
|
|
}
|
|
|
|
bool PropertyCascade::addNormalMatches(CascadeLevel cascadeLevel)
|
|
{
|
|
bool hasImportant = false;
|
|
for (auto& matchedDeclarations : declarationsForCascadeLevel(m_matchResult, cascadeLevel))
|
|
hasImportant |= addMatch(matchedDeclarations, cascadeLevel, false);
|
|
|
|
return hasImportant;
|
|
}
|
|
|
|
static bool hasImportantProperties(const StyleProperties& properties)
|
|
{
|
|
for (unsigned i = 0, count = properties.propertyCount(); i < count; ++i) {
|
|
if (properties.propertyAt(i).isImportant())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PropertyCascade::addImportantMatches(CascadeLevel cascadeLevel)
|
|
{
|
|
struct IndexAndOrdinal {
|
|
unsigned index;
|
|
ScopeOrdinal ordinal;
|
|
};
|
|
Vector<IndexAndOrdinal> importantMatches;
|
|
bool hasMatchesFromOtherScopes = false;
|
|
|
|
auto& matchedDeclarations = declarationsForCascadeLevel(m_matchResult, cascadeLevel);
|
|
|
|
for (unsigned i = 0; i < matchedDeclarations.size(); ++i) {
|
|
const MatchedProperties& matchedProperties = matchedDeclarations[i];
|
|
|
|
if (!hasImportantProperties(*matchedProperties.properties))
|
|
continue;
|
|
|
|
importantMatches.append({ i, matchedProperties.styleScopeOrdinal });
|
|
|
|
if (matchedProperties.styleScopeOrdinal != ScopeOrdinal::Element)
|
|
hasMatchesFromOtherScopes = true;
|
|
}
|
|
|
|
if (importantMatches.isEmpty())
|
|
return;
|
|
|
|
if (hasMatchesFromOtherScopes) {
|
|
// For !important properties a later shadow tree wins.
|
|
// Match results are sorted in reverse tree context order so this is not needed for normal properties.
|
|
std::stable_sort(importantMatches.begin(), importantMatches.end(), [] (const IndexAndOrdinal& a, const IndexAndOrdinal& b) {
|
|
return a.ordinal < b.ordinal;
|
|
});
|
|
}
|
|
|
|
for (auto& match : importantMatches)
|
|
addMatch(matchedDeclarations[match.index], cascadeLevel, true);
|
|
}
|
|
|
|
const PropertyCascade* PropertyCascade::propertyCascadeForRollback(CascadeLevel cascadeLevel) const
|
|
{
|
|
switch (cascadeLevel) {
|
|
case CascadeLevel::Author:
|
|
if (!m_authorRollbackCascade) {
|
|
auto cascadeLevels = OptionSet<CascadeLevel> { CascadeLevel::UserAgent, CascadeLevel::User };
|
|
m_authorRollbackCascade = makeUnique<const PropertyCascade>(*this, cascadeLevels);
|
|
}
|
|
return m_authorRollbackCascade.get();
|
|
|
|
case CascadeLevel::User:
|
|
if (!m_userRollbackCascade) {
|
|
auto cascadeLevels = OptionSet<CascadeLevel> { CascadeLevel::UserAgent };
|
|
m_userRollbackCascade = makeUnique<const PropertyCascade>(*this, cascadeLevels);
|
|
}
|
|
return m_userRollbackCascade.get();
|
|
|
|
case CascadeLevel::UserAgent:
|
|
return nullptr;
|
|
}
|
|
ASSERT_NOT_REACHED();
|
|
return nullptr;
|
|
}
|
|
|
|
PropertyCascade::Direction PropertyCascade::resolveDirectionAndWritingMode(Direction inheritedDirection) const
|
|
{
|
|
Direction result = inheritedDirection;
|
|
|
|
bool hadImportantWritingMode = false;
|
|
bool hadImportantDirection = false;
|
|
|
|
for (auto cascadeLevel : { CascadeLevel::UserAgent, CascadeLevel::User, CascadeLevel::Author }) {
|
|
for (const auto& matchedProperties : declarationsForCascadeLevel(m_matchResult, cascadeLevel)) {
|
|
for (unsigned i = 0, count = matchedProperties.properties->propertyCount(); i < count; ++i) {
|
|
auto property = matchedProperties.properties->propertyAt(i);
|
|
if (!property.value()->isPrimitiveValue())
|
|
continue;
|
|
switch (property.id()) {
|
|
case CSSPropertyWritingMode:
|
|
if (!hadImportantWritingMode || property.isImportant()) {
|
|
result.writingMode = downcast<CSSPrimitiveValue>(*property.value());
|
|
hadImportantWritingMode = property.isImportant();
|
|
}
|
|
break;
|
|
case CSSPropertyDirection:
|
|
if (!hadImportantDirection || property.isImportant()) {
|
|
result.textDirection = downcast<CSSPrimitiveValue>(*property.value());
|
|
hadImportantDirection = property.isImportant();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
PropertyCascade::Direction PropertyCascade::direction() const
|
|
{
|
|
if (m_directionIsUnresolved) {
|
|
m_direction = resolveDirectionAndWritingMode(m_direction);
|
|
m_directionIsUnresolved = false;
|
|
}
|
|
return m_direction;
|
|
}
|
|
|
|
}
|
|
}
|