blob: a53e9af01558df3696bdc8ca3e303061d8f5308b [file] [log] [blame]
/*
* Copyright (C) 2022 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. ``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
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* 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 "StyleCustomPropertyRegistry.h"
#include "CSSPropertyParser.h"
#include "CSSRegisteredCustomProperty.h"
#include "ComputedStyleDependencies.h"
#include "Document.h"
#include "Element.h"
#include "KeyframeEffect.h"
#include "NodeDocument.h"
#include "RenderStyle+SettersInlines.h"
#include "StyleBuilder.h"
#include "StyleCustomProperty.h"
#include "StyleResolver.h"
#include "StyleScope.h"
#include "WebAnimation.h"
#include <wtf/TZoneMallocInlines.h>
namespace WebCore {
namespace Style {
WTF_MAKE_TZONE_ALLOCATED_IMPL(CustomPropertyRegistry);
CustomPropertyRegistry::CustomPropertyRegistry(Scope& scope)
: m_scope(scope)
, m_initialValuePrototypeStyle(RenderStyle::createPtr())
{
}
const CSSRegisteredCustomProperty* CustomPropertyRegistry::get(const AtomString& name) const
{
ASSERT(isCustomPropertyName(name));
// API wins.
// https://drafts.css-houdini.org/css-properties-values-api/#determining-registration
if (auto* property = m_propertiesFromAPI.get(name))
return property;
return m_propertiesFromStylesheet.get(name);
}
bool CustomPropertyRegistry::isInherited(const AtomString& name) const
{
auto* registered = get(name);
return registered ? registered->inherits : true;
}
bool CustomPropertyRegistry::registerFromAPI(CSSRegisteredCustomProperty&& property)
{
// First registration wins.
// https://drafts.css-houdini.org/css-properties-values-api/#determining-registration
auto success = m_propertiesFromAPI.ensure(property.name, [&] {
return makeUniqueRef<CSSRegisteredCustomProperty>(WTF::move(property));
}).isNewEntry;
if (success) {
invalidate(property.name);
m_scope.didChangeStyleSheetEnvironment();
}
return success;
}
void CustomPropertyRegistry::registerFromStylesheet(const StyleRuleProperty::Descriptor& descriptor)
{
auto syntax = CSSCustomPropertySyntax::parse(descriptor.syntax);
ASSERT(syntax);
auto& document = m_scope.document();
RefPtr<const Style::CustomProperty> initialValue;
RefPtr<CSSVariableData> initialValueTokensForViewportUnits;
if (descriptor.initialValue) {
auto tokenRange = descriptor.initialValue->tokenRange();
auto parsedInitialValue = parseInitialValue(document, descriptor.name, *syntax, tokenRange);
if (!parsedInitialValue)
return;
initialValue = parsedInitialValue->first;
if (parsedInitialValue->second == ViewportUnitDependency::Yes) {
initialValueTokensForViewportUnits = CSSVariableData::create(tokenRange);
m_scope.document().setHasStyleWithViewportUnits();
}
} else if (syntax->isUniversal()) {
// "If the value of the syntax descriptor is the universal syntax definition, then the initial-value descriptor is optional.
// If omitted, the initial value of the property is the guaranteed-invalid value."
// https://drafts.css-houdini.org/css-properties-values-api/#initial-value-descriptor
initialValue = CustomProperty::createForGuaranteedInvalid(descriptor.name);
} else {
// Should have been discarded at parse-time.
ASSERT_NOT_REACHED();
return;
}
auto property = CSSRegisteredCustomProperty {
AtomString { descriptor.name },
*syntax,
*descriptor.inherits,
WTF::move(initialValue),
WTF::move(initialValueTokensForViewportUnits)
};
// Last rule wins.
// https://drafts.css-houdini.org/css-properties-values-api/#determining-registration
m_propertiesFromStylesheet.set(property.name, makeUniqueRef<CSSRegisteredCustomProperty>(WTF::move(property)));
invalidate(property.name);
}
void CustomPropertyRegistry::clearRegisteredFromStylesheets()
{
if (m_propertiesFromStylesheet.isEmpty())
return;
m_propertiesFromStylesheet.clear();
invalidate(nullAtom());
}
const RenderStyle& CustomPropertyRegistry::initialValuePrototypeStyle() const
{
if (m_hasInvalidPrototypeStyle) {
m_hasInvalidPrototypeStyle = false;
auto oldStyle = std::exchange(m_initialValuePrototypeStyle, RenderStyle::createPtr());
auto initializeToStyle = [&](auto& map) {
for (auto& property : map.values()) {
if (property->initialValue && !property->initialValue->isGuaranteedInvalid())
m_initialValuePrototypeStyle->setCustomPropertyValue(*property->initialValue, property->inherits);
}
};
initializeToStyle(m_propertiesFromStylesheet);
initializeToStyle(m_propertiesFromAPI);
m_initialValuePrototypeStyle->deduplicateCustomProperties(*oldStyle);
}
return *m_initialValuePrototypeStyle;
}
void CustomPropertyRegistry::invalidate(const AtomString& customProperty)
{
m_hasInvalidPrototypeStyle = true;
// Changing property registration may affect computed property values in the cache.
m_scope.invalidateMatchedDeclarationsCache();
// FIXME: Invalidate all?
if (!customProperty.isNull())
notifyAnimationsOfCustomPropertyRegistration(customProperty);
}
void CustomPropertyRegistry::notifyAnimationsOfCustomPropertyRegistration(const AtomString& customProperty)
{
auto& document = m_scope.document();
for (auto& animation : WebAnimation::instances()) {
if (RefPtr keyframeEffect = animation->keyframeEffect()) {
if (RefPtr target = keyframeEffect->target()) {
if (&target->document() == &document)
keyframeEffect->customPropertyRegistrationDidChange(customProperty);
}
}
}
}
auto CustomPropertyRegistry::parseInitialValue(const Document& document, const AtomString& propertyName, const CSSCustomPropertySyntax& syntax, CSSParserTokenRange tokenRange) -> Expected<std::pair<RefPtr<const CustomProperty>, ViewportUnitDependency>, ParseInitialValueError>
{
// FIXME: This parses twice.
auto dependencies = CSSPropertyParser::collectParsedCustomPropertyValueDependencies(syntax, tokenRange, document.cssParserContext());
if (!dependencies.isComputationallyIndependent())
return makeUnexpected(ParseInitialValueError::NotComputationallyIndependent);
// We don't need to provide a real context style since only computationally independent values are allowed (no 'em' etc).
auto placeholderStyle = RenderStyle::create();
auto dummyState = Style::BuilderState::create(placeholderStyle, { &document });
auto initialValue = CSSPropertyParser::parseTypedCustomPropertyInitialValue(propertyName, syntax, tokenRange, dummyState, { document });
if (!initialValue)
return makeUnexpected(ParseInitialValueError::DidNotParse);
return std::pair { WTF::move(initialValue), dependencies.viewportDimensions ? ViewportUnitDependency::Yes : ViewportUnitDependency::No };
}
bool CustomPropertyRegistry::invalidatePropertiesWithViewportUnits(Document& document)
{
bool invalidatedAny = false;
auto invalidatePropertiesWithViewportUnits = [&](auto& map) {
for (auto& property : map.values()) {
if (!property->initialValueTokensForViewportUnits)
continue;
auto tokenRange = property->initialValueTokensForViewportUnits->tokenRange();
auto parsedInitialValue = parseInitialValue(document, property->name, property->syntax, tokenRange);
if (!parsedInitialValue) {
ASSERT_NOT_REACHED();
continue;
}
property->initialValue = WTF::move(parsedInitialValue->first);
ASSERT(parsedInitialValue->second == ViewportUnitDependency::Yes);
invalidate(property->name);
invalidatedAny = true;
}
};
invalidatePropertiesWithViewportUnits(m_propertiesFromAPI);
invalidatePropertiesWithViewportUnits(m_propertiesFromStylesheet);
return invalidatedAny;
}
}
}