/* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * Copyright (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) * Copyright (C) 2005-2019 Apple Inc. All rights reserved. * Copyright (C) 2007 Alexey Proskuryakov * Copyright (C) 2007, 2008 Eric Seidel * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (c) 2011, Code Aurora Forum. All rights reserved. * Copyright (C) Research In Motion Limited 2011. All rights reserved. * Copyright (C) 2012, 2013 Google Inc. All rights reserved. * Copyright (C) 2014 Igalia S.L. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "StyleResolver.h" #include "CSSFontSelector.h" #include "CSSKeyframeRule.h" #include "CSSKeyframesRule.h" #include "CSSParser.h" #include "CSSPrimitiveValueMappings.h" #include "CSSPropertyNames.h" #include "CSSSelector.h" #include "CSSStyleRule.h" #include "CSSStyleSheet.h" #include "CachedResourceLoader.h" #include "ElementRuleCollector.h" #include "Frame.h" #include "FrameSelection.h" #include "FrameView.h" #include "InspectorInstrumentation.h" #include "KeyframeList.h" #include "Logging.h" #include "MediaList.h" #include "MediaQueryEvaluator.h" #include "NodeRenderStyle.h" #include "PageRuleCollector.h" #include "Pair.h" #include "RenderScrollbar.h" #include "RenderStyleConstants.h" #include "RenderView.h" #include "RuleSet.h" #include "RuntimeEnabledFeatures.h" #include "SVGDocumentExtensions.h" #include "SVGElement.h" #include "SVGFontFaceElement.h" #include "Settings.h" #include "ShadowRoot.h" #include "SharedStringHash.h" #include "StyleAdjuster.h" #include "StyleBuilder.h" #include "StyleFontSizeFunctions.h" #include "StyleProperties.h" #include "StylePropertyShorthand.h" #include "StyleResolveForDocument.h" #include "StyleRule.h" #include "StyleSheetContents.h" #include "UserAgentStyle.h" #include "VisitedLinkState.h" #include "WebKitFontFamilyNames.h" #include #include #include #include #include namespace WebCore { namespace Style { using namespace HTMLNames; WTF_MAKE_ISO_ALLOCATED_IMPL(Resolver); Ref Resolver::create(Document& document) { return adoptRef(*new Resolver(document)); } Resolver::Resolver(Document& document) : m_ruleSets(*this) , m_document(document) , m_matchAuthorAndUserStyles(m_document.settings().authorAndUserStylesEnabled()) { UserAgentStyle::initDefaultStyleSheet(); // construct document root element default style. this is needed // to evaluate media queries that contain relative constraints, like "screen and (max-width: 10em)" // This is here instead of constructor, because when constructor is run, // document doesn't have documentElement // NOTE: this assumes that element that gets passed to styleForElement -call // is always from the document that owns the style selector FrameView* view = m_document.view(); if (view) m_mediaQueryEvaluator = MediaQueryEvaluator { view->mediaType() }; else m_mediaQueryEvaluator = MediaQueryEvaluator { }; if (auto* documentElement = m_document.documentElement()) { m_rootDefaultStyle = styleForElement(*documentElement, m_document.renderStyle(), nullptr, RuleMatchingBehavior::MatchOnlyUserAgentRules).renderStyle; // Turn off assertion against font lookups during style resolver initialization. We may need root style font for media queries. m_document.fontSelector().incrementIsComputingRootStyleFont(); m_rootDefaultStyle->fontCascade().update(&m_document.fontSelector()); m_rootDefaultStyle->fontCascade().primaryFont(); m_document.fontSelector().decrementIsComputingRootStyleFont(); } if (m_rootDefaultStyle && view) m_mediaQueryEvaluator = MediaQueryEvaluator { view->mediaType(), m_document, m_rootDefaultStyle.get() }; m_ruleSets.resetAuthorStyle(); m_ruleSets.resetUserAgentMediaQueryStyle(); } void Resolver::addCurrentSVGFontFaceRules() { if (m_document.svgExtensions()) { auto& svgFontFaceElements = m_document.svgExtensions()->svgFontFaceElements(); for (auto& svgFontFaceElement : svgFontFaceElements) m_document.fontSelector().addFontFaceRule(svgFontFaceElement.fontFaceRule(), svgFontFaceElement.isInUserAgentShadowTree()); } } void Resolver::appendAuthorStyleSheets(const Vector>& styleSheets) { m_ruleSets.appendAuthorStyleSheets(styleSheets, &m_mediaQueryEvaluator, m_inspectorCSSOMWrappers); if (auto renderView = document().renderView()) renderView->style().fontCascade().update(&document().fontSelector()); } // This is a simplified style setting function for keyframe styles void Resolver::addKeyframeStyle(Ref&& rule) { AtomString s(rule->name()); m_keyframesRuleMap.set(s.impl(), WTFMove(rule)); } Resolver::~Resolver() { RELEASE_ASSERT(!m_document.isResolvingTreeStyle()); RELEASE_ASSERT(!m_isDeleted); m_isDeleted = true; } Resolver::State::State(const Element& element, const RenderStyle* parentStyle, const RenderStyle* documentElementStyle) : m_element(&element) , m_parentStyle(parentStyle) { bool resetStyleInheritance = hasShadowRootParent(element) && downcast(element.parentNode())->resetStyleInheritance(); if (resetStyleInheritance) m_parentStyle = nullptr; auto& document = element.document(); auto* documentElement = document.documentElement(); if (!documentElement || documentElement == &element) m_rootElementStyle = document.renderStyle(); else m_rootElementStyle = documentElementStyle ? documentElementStyle : documentElement->renderStyle(); } inline void Resolver::State::setStyle(std::unique_ptr style) { m_style = WTFMove(style); } inline void Resolver::State::setParentStyle(std::unique_ptr parentStyle) { m_ownedParentStyle = WTFMove(parentStyle); m_parentStyle = m_ownedParentStyle.get(); } static inline bool isAtShadowBoundary(const Element& element) { return is(element.parentNode()); } BuilderContext Resolver::builderContext(const State& state) { return { m_document, *state.parentStyle(), state.rootElementStyle(), state.element() }; } ElementStyle Resolver::styleForElement(const Element& element, const RenderStyle* parentStyle, const RenderStyle* parentBoxStyle, RuleMatchingBehavior matchingBehavior, const SelectorFilter* selectorFilter) { RELEASE_ASSERT(!m_isDeleted); auto state = State(element, parentStyle, m_overrideDocumentElementStyle); if (state.parentStyle()) { state.setStyle(RenderStyle::createPtr()); state.style()->inheritFrom(*state.parentStyle()); } else { state.setStyle(defaultStyleForElement(&element)); state.setParentStyle(RenderStyle::clonePtr(*state.style())); } auto& style = *state.style(); if (element.isLink()) { style.setIsLink(true); InsideLink linkState = document().visitedLinkState().determineLinkState(element); if (linkState != InsideLink::NotInside) { bool forceVisited = InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassVisited); if (forceVisited) linkState = InsideLink::InsideVisited; } style.setInsideLink(linkState); } UserAgentStyle::ensureDefaultStyleSheetsForElement(element); ElementRuleCollector collector(element, m_ruleSets, selectorFilter); collector.setMedium(&m_mediaQueryEvaluator); if (matchingBehavior == RuleMatchingBehavior::MatchOnlyUserAgentRules) collector.matchUARules(); else collector.matchAllRules(m_matchAuthorAndUserStyles, matchingBehavior != RuleMatchingBehavior::MatchAllRulesExcludingSMIL); if (collector.matchedPseudoElementIds()) style.setHasPseudoStyles(collector.matchedPseudoElementIds()); // This is required for style sharing. if (collector.didMatchUncommonAttributeSelector()) style.setUnique(); auto elementStyleRelations = commitRelationsToRenderStyle(style, element, collector.styleRelations()); applyMatchedProperties(state, collector.matchResult()); Adjuster adjuster(document(), *state.parentStyle(), parentBoxStyle, &element); adjuster.adjust(*state.style(), state.userAgentAppearanceStyle()); if (state.style()->hasViewportUnits()) document().setHasStyleWithViewportUnits(); return { state.takeStyle(), WTFMove(elementStyleRelations) }; } std::unique_ptr Resolver::styleForKeyframe(const Element& element, const RenderStyle* elementStyle, const RenderStyle* parentElementStyle, const StyleRuleKeyframe* keyframe, KeyframeValue& keyframeValue) { RELEASE_ASSERT(!m_isDeleted); MatchResult result; result.authorDeclarations.append({ &keyframe->properties() }); auto state = State(element, nullptr, m_overrideDocumentElementStyle); state.setStyle(RenderStyle::clonePtr(*elementStyle)); state.setParentStyle(RenderStyle::clonePtr(parentElementStyle ? *parentElementStyle : *elementStyle)); Builder builder(*state.style(), builderContext(state), result, { CascadeLevel::Author }); builder.applyAllProperties(); Adjuster adjuster(document(), *state.parentStyle(), nullptr, nullptr); adjuster.adjust(*state.style(), state.userAgentAppearanceStyle()); // Add all the animating properties to the keyframe. unsigned propertyCount = keyframe->properties().propertyCount(); for (unsigned i = 0; i < propertyCount; ++i) { CSSPropertyID property = keyframe->properties().propertyAt(i).id(); // Timing-function within keyframes is special, because it is not animated; it just // describes the timing function between this keyframe and the next. if (property != CSSPropertyAnimationTimingFunction) keyframeValue.addProperty(property); } return state.takeStyle(); } bool Resolver::isAnimationNameValid(const String& name) { return m_keyframesRuleMap.find(AtomString(name).impl()) != m_keyframesRuleMap.end(); } void Resolver::keyframeStylesForAnimation(const Element& element, const RenderStyle* elementStyle, const RenderStyle* parentElementStyle, KeyframeList& list) { list.clear(); // Get the keyframesRule for this name. if (list.animationName().isEmpty()) return; m_keyframesRuleMap.checkConsistency(); KeyframesRuleMap::iterator it = m_keyframesRuleMap.find(list.animationName().impl()); if (it == m_keyframesRuleMap.end()) return; const StyleRuleKeyframes* keyframesRule = it->value.get(); auto* keyframes = &keyframesRule->keyframes(); Vector> newKeyframesIfNecessary; bool hasDuplicateKeys = false; HashSet keyframeKeys; for (auto& keyframe : *keyframes) { for (auto key : keyframe->keys()) { if (!keyframeKeys.add(key)) { hasDuplicateKeys = true; break; } } if (hasDuplicateKeys) break; } // FIXME: If HashMaps could have Ref<> as value types, we wouldn't need // to copy the HashMap into a Vector. if (hasDuplicateKeys) { // Merge duplicate key times. HashMap> keyframesMap; for (auto& originalKeyframe : keyframesRule->keyframes()) { for (auto key : originalKeyframe->keys()) { if (auto keyframe = keyframesMap.get(key)) keyframe->mutableProperties().mergeAndOverrideOnConflict(originalKeyframe->properties()); else { auto StyleRuleKeyframe = StyleRuleKeyframe::create(MutableStyleProperties::create()); StyleRuleKeyframe.ptr()->setKey(key); StyleRuleKeyframe.ptr()->mutableProperties().mergeAndOverrideOnConflict(originalKeyframe->properties()); keyframesMap.set(key, StyleRuleKeyframe.ptr()); } } } for (auto& keyframe : keyframesMap.values()) newKeyframesIfNecessary.append(*keyframe.get()); keyframes = &newKeyframesIfNecessary; } // Construct and populate the style for each keyframe. for (auto& keyframe : *keyframes) { // Add this keyframe style to all the indicated key times for (auto key : keyframe->keys()) { KeyframeValue keyframeValue(0, nullptr); keyframeValue.setStyle(styleForKeyframe(element, elementStyle, parentElementStyle, keyframe.ptr(), keyframeValue)); keyframeValue.setKey(key); if (auto timingFunctionCSSValue = keyframe->properties().getPropertyCSSValue(CSSPropertyAnimationTimingFunction)) keyframeValue.setTimingFunction(TimingFunction::createFromCSSValue(*timingFunctionCSSValue.get())); list.insert(WTFMove(keyframeValue)); } } list.fillImplicitKeyframes(element, *this, elementStyle, parentElementStyle); } std::unique_ptr Resolver::pseudoStyleForElement(const Element& element, const PseudoElementRequest& pseudoElementRequest, const RenderStyle& parentStyle, const RenderStyle* parentBoxStyle, const SelectorFilter* selectorFilter) { auto state = State(element, &parentStyle, m_overrideDocumentElementStyle); if (state.parentStyle()) { state.setStyle(RenderStyle::createPtr()); state.style()->inheritFrom(*state.parentStyle()); } else { state.setStyle(defaultStyleForElement(&element)); state.setParentStyle(RenderStyle::clonePtr(*state.style())); } ElementRuleCollector collector(element, m_ruleSets, selectorFilter); collector.setPseudoElementRequest(pseudoElementRequest); collector.setMedium(&m_mediaQueryEvaluator); collector.matchUARules(); if (m_matchAuthorAndUserStyles) { collector.matchUserRules(); collector.matchAuthorRules(); } ASSERT(!collector.matchedPseudoElementIds()); if (collector.matchResult().isEmpty()) return nullptr; state.style()->setStyleType(pseudoElementRequest.pseudoId); applyMatchedProperties(state, collector.matchResult()); Adjuster adjuster(document(), *state.parentStyle(), parentBoxStyle, nullptr); adjuster.adjust(*state.style(), state.userAgentAppearanceStyle()); if (state.style()->hasViewportUnits()) document().setHasStyleWithViewportUnits(); return state.takeStyle(); } std::unique_ptr Resolver::styleForPage(int pageIndex) { RELEASE_ASSERT(!m_isDeleted); auto* documentElement = m_document.documentElement(); if (!documentElement) return RenderStyle::createPtr(); auto state = State(*documentElement, m_document.renderStyle()); state.setStyle(RenderStyle::createPtr()); state.style()->inheritFrom(*state.rootElementStyle()); PageRuleCollector collector(state, m_ruleSets); collector.matchAllPageRules(pageIndex); auto& result = collector.matchResult(); Builder builder(*state.style(), builderContext(state), result, { CascadeLevel::Author }); builder.applyAllProperties(); // Now return the style. return state.takeStyle(); } std::unique_ptr Resolver::defaultStyleForElement(const Element* element) { auto style = RenderStyle::createPtr(); FontCascadeDescription fontDescription; fontDescription.setRenderingMode(settings().fontRenderingMode()); fontDescription.setOneFamily(standardFamily); fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium); auto size = fontSizeForKeyword(CSSValueMedium, false, document()); fontDescription.setSpecifiedSize(size); fontDescription.setComputedSize(computedFontSizeFromSpecifiedSize(size, fontDescription.isAbsoluteSize(), is(element), style.get(), document())); fontDescription.setShouldAllowUserInstalledFonts(settings().shouldAllowUserInstalledFonts() ? AllowUserInstalledFonts::Yes : AllowUserInstalledFonts::No); style->setFontDescription(WTFMove(fontDescription)); style->fontCascade().update(&document().fontSelector()); return style; } Vector> Resolver::styleRulesForElement(const Element* element, unsigned rulesToInclude) { return pseudoStyleRulesForElement(element, PseudoId::None, rulesToInclude); } Vector> Resolver::pseudoStyleRulesForElement(const Element* element, PseudoId pseudoId, unsigned rulesToInclude) { if (!element) return { }; auto state = State(*element, nullptr); ElementRuleCollector collector(*element, m_ruleSets, nullptr); collector.setMode(SelectorChecker::Mode::CollectingRules); collector.setPseudoElementRequest({ pseudoId }); collector.setMedium(&m_mediaQueryEvaluator); collector.setIncludeEmptyRules(rulesToInclude & EmptyCSSRules); if (rulesToInclude & UAAndUserCSSRules) { // First we match rules from the user agent sheet. collector.matchUARules(); // Now we check user sheet rules. if (m_matchAuthorAndUserStyles) collector.matchUserRules(); } if (m_matchAuthorAndUserStyles && (rulesToInclude & AuthorCSSRules)) collector.matchAuthorRules(); return collector.matchedRuleList(); } static bool elementTypeHasAppearanceFromUAStyle(const Element& element) { // NOTE: This is just a hard-coded list of elements that have some -webkit-appearance value in html.css const auto& localName = element.localName(); return localName == HTMLNames::inputTag || localName == HTMLNames::textareaTag || localName == HTMLNames::buttonTag || localName == HTMLNames::progressTag || localName == HTMLNames::selectTag || localName == HTMLNames::meterTag; } void Resolver::invalidateMatchedDeclarationsCache() { m_matchedDeclarationsCache.invalidate(); } void Resolver::clearCachedDeclarationsAffectedByViewportUnits() { m_matchedDeclarationsCache.clearEntriesAffectedByViewportUnits(); } void Resolver::applyMatchedProperties(State& state, const MatchResult& matchResult, UseMatchedDeclarationsCache useMatchedDeclarationsCache) { unsigned cacheHash = useMatchedDeclarationsCache == UseMatchedDeclarationsCache::Yes ? MatchedDeclarationsCache::computeHash(matchResult) : 0; auto includedProperties = PropertyCascade::IncludedProperties::All; auto& style = *state.style(); auto& parentStyle = *state.parentStyle(); auto& element = *state.element(); auto* cacheEntry = m_matchedDeclarationsCache.find(cacheHash, matchResult); if (cacheEntry && MatchedDeclarationsCache::isCacheable(element, style, parentStyle)) { // We can build up the style by copying non-inherited properties from an earlier style object built using the same exact // style declarations. We then only need to apply the inherited properties, if any, as their values can depend on the // element context. This is fast and saves memory by reusing the style data structures. style.copyNonInheritedFrom(*cacheEntry->renderStyle); if (parentStyle.inheritedEqual(*cacheEntry->parentRenderStyle) && !isAtShadowBoundary(element)) { InsideLink linkStatus = state.style()->insideLink(); // If the cache item parent style has identical inherited properties to the current parent style then the // resulting style will be identical too. We copy the inherited properties over from the cache and are done. style.inheritFrom(*cacheEntry->renderStyle); // Unfortunately the link status is treated like an inherited property. We need to explicitly restore it. style.setInsideLink(linkStatus); return; } includedProperties = PropertyCascade::IncludedProperties::InheritedOnly; } if (elementTypeHasAppearanceFromUAStyle(element)) { // Find out if there's a -webkit-appearance property in effect from the UA sheet. // If so, we cache the border and background styles so that RenderTheme::adjustStyle() // can look at them later to figure out if this is a styled form control or not. auto userAgentStyle = RenderStyle::clonePtr(style); Builder builder(*userAgentStyle, builderContext(state), matchResult, { CascadeLevel::UserAgent }); builder.applyAllProperties(); state.setUserAgentAppearanceStyle(WTFMove(userAgentStyle)); } Builder builder(*state.style(), builderContext(state), matchResult, allCascadeLevels(), includedProperties); // High priority properties may affect resolution of other properties (they are mostly font related). builder.applyHighPriorityProperties(); if (cacheEntry && !cacheEntry->isUsableAfterHighPriorityProperties(style)) { // We need to resolve all properties without caching. applyMatchedProperties(state, matchResult, UseMatchedDeclarationsCache::No); return; } builder.applyLowPriorityProperties(); for (auto& contentAttribute : builder.state().registeredContentAttributes()) ruleSets().mutableFeatures().registerContentAttribute(contentAttribute); if (cacheEntry || !cacheHash) return; if (MatchedDeclarationsCache::isCacheable(element, style, parentStyle)) m_matchedDeclarationsCache.add(style, parentStyle, cacheHash, matchResult); } bool Resolver::hasViewportDependentMediaQueries() const { return m_ruleSets.hasViewportDependentMediaQueries(); } std::optional Resolver::evaluateDynamicMediaQueries() { return m_ruleSets.evaluateDynamicMediaQueryRules(m_mediaQueryEvaluator); } } // namespace Style } // namespace WebCore