1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005-2014 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
11 * Copyright (C) 2012, 2013 Google Inc. All rights reserved.
12 * Copyright (C) 2014 Igalia S.L.
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Library General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public License
25 * along with this library; see the file COPYING.LIB. If not, write to
26 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27 * Boston, MA 02110-1301, USA.
28 */
29
30#include "config.h"
31#include "StyleResolver.h"
32
33#include "CSSCalculationValue.h"
34#include "CSSCursorImageValue.h"
35#include "CSSCustomPropertyValue.h"
36#include "CSSDefaultStyleSheets.h"
37#include "CSSFilterImageValue.h"
38#include "CSSFontSelector.h"
39#include "CSSFunctionValue.h"
40#include "CSSGradientValue.h"
41#include "CSSImageSetValue.h"
42#include "CSSImageValue.h"
43#include "CSSKeyframeRule.h"
44#include "CSSKeyframesRule.h"
45#include "CSSPaintImageValue.h"
46#include "CSSParser.h"
47#include "CSSPrimitiveValueMappings.h"
48#include "CSSPropertyNames.h"
49#include "CSSReflectValue.h"
50#include "CSSSelector.h"
51#include "CSSShadowValue.h"
52#include "CSSStyleRule.h"
53#include "CSSStyleSheet.h"
54#include "CSSValueList.h"
55#include "CSSValuePool.h"
56#include "CachedResourceLoader.h"
57#include "ElementRuleCollector.h"
58#include "FilterOperation.h"
59#include "Frame.h"
60#include "FrameSelection.h"
61#include "FrameView.h"
62#include "HTMLInputElement.h"
63#include "HTMLMarqueeElement.h"
64#include "HTMLNames.h"
65#include "HTMLSlotElement.h"
66#include "HTMLTableElement.h"
67#include "HTMLTextAreaElement.h"
68#include "InspectorInstrumentation.h"
69#include "KeyframeList.h"
70#include "Logging.h"
71#include "MathMLElement.h"
72#include "MathMLNames.h"
73#include "MediaList.h"
74#include "MediaQueryEvaluator.h"
75#include "NodeRenderStyle.h"
76#include "PageRuleCollector.h"
77#include "PaintWorkletGlobalScope.h"
78#include "Pair.h"
79#include "Quirks.h"
80#include "RenderScrollbar.h"
81#include "RenderStyleConstants.h"
82#include "RenderTheme.h"
83#include "RenderView.h"
84#include "RuleSet.h"
85#include "RuntimeEnabledFeatures.h"
86#include "SVGDocument.h"
87#include "SVGDocumentExtensions.h"
88#include "SVGFontFaceElement.h"
89#include "SVGNames.h"
90#include "SVGSVGElement.h"
91#include "SVGURIReference.h"
92#include "Settings.h"
93#include "ShadowRoot.h"
94#include "SharedStringHash.h"
95#include "StyleBuilder.h"
96#include "StyleColor.h"
97#include "StyleCachedImage.h"
98#include "StyleFontSizeFunctions.h"
99#include "StyleGeneratedImage.h"
100#include "StyleProperties.h"
101#include "StylePropertyShorthand.h"
102#include "StyleRule.h"
103#include "StyleSheetContents.h"
104#include "TransformFunctions.h"
105#include "TransformOperations.h"
106#include "UserAgentStyleSheets.h"
107#include "ViewportStyleResolver.h"
108#include "VisitedLinkState.h"
109#include "WebKitFontFamilyNames.h"
110#include <bitset>
111#include <wtf/Seconds.h>
112#include <wtf/StdLibExtras.h>
113#include <wtf/Vector.h>
114#include <wtf/text/AtomStringHash.h>
115
116namespace WebCore {
117
118using namespace HTMLNames;
119
120static const CSSPropertyID firstLowPriorityProperty = static_cast<CSSPropertyID>(lastHighPriorityProperty + 1);
121
122static void extractDirectionAndWritingMode(const RenderStyle&, const StyleResolver::MatchResult&, TextDirection&, WritingMode&);
123
124inline void StyleResolver::State::cacheBorderAndBackground()
125{
126 m_hasUAAppearance = m_style->hasAppearance();
127 if (m_hasUAAppearance) {
128 m_borderData = m_style->border();
129 m_backgroundData = m_style->backgroundLayers();
130 m_backgroundColor = m_style->backgroundColor();
131 }
132}
133
134inline void StyleResolver::State::clear()
135{
136 m_element = nullptr;
137 m_parentStyle = nullptr;
138 m_ownedParentStyle = nullptr;
139 m_cssToLengthConversionData = CSSToLengthConversionData();
140}
141
142void StyleResolver::MatchResult::addMatchedProperties(const StyleProperties& properties, StyleRule* rule, unsigned linkMatchType, PropertyWhitelistType propertyWhitelistType, Style::ScopeOrdinal styleScopeOrdinal)
143{
144 m_matchedProperties.grow(m_matchedProperties.size() + 1);
145 StyleResolver::MatchedProperties& newProperties = m_matchedProperties.last();
146 newProperties.properties = const_cast<StyleProperties*>(&properties);
147 newProperties.linkMatchType = linkMatchType;
148 newProperties.whitelistType = propertyWhitelistType;
149 newProperties.styleScopeOrdinal = styleScopeOrdinal;
150 matchedRules.append(rule);
151
152 if (styleScopeOrdinal != Style::ScopeOrdinal::Element)
153 isCacheable = false;
154
155 if (isCacheable) {
156 for (unsigned i = 0, count = properties.propertyCount(); i < count; ++i) {
157 // Currently the property cache only copy the non-inherited values and resolve
158 // the inherited ones.
159 // Here we define some exception were we have to resolve some properties that are not inherited
160 // by default. If those exceptions become too common on the web, it should be possible
161 // to build a list of exception to resolve instead of completely disabling the cache.
162
163 StyleProperties::PropertyReference current = properties.propertyAt(i);
164 if (!current.isInherited()) {
165 // If the property value is explicitly inherited, we need to apply further non-inherited properties
166 // as they might override the value inherited here. For this reason we don't allow declarations with
167 // explicitly inherited properties to be cached.
168 const CSSValue& value = *current.value();
169 if (value.isInheritedValue()) {
170 isCacheable = false;
171 break;
172 }
173
174 // The value currentColor has implicitely the same side effect. It depends on the value of color,
175 // which is an inherited value, making the non-inherited property implicitly inherited.
176 if (is<CSSPrimitiveValue>(value) && downcast<CSSPrimitiveValue>(value).valueID() == CSSValueCurrentcolor) {
177 isCacheable = false;
178 break;
179 }
180
181 if (value.hasVariableReferences()) {
182 isCacheable = false;
183 break;
184 }
185 }
186 }
187 }
188}
189
190StyleResolver::StyleResolver(Document& document)
191 : m_ruleSets(*this)
192 , m_matchedPropertiesCacheSweepTimer(*this, &StyleResolver::sweepMatchedPropertiesCache)
193 , m_document(document)
194#if ENABLE(CSS_DEVICE_ADAPTATION)
195 , m_viewportStyleResolver(ViewportStyleResolver::create(&document))
196#endif
197 , m_styleMap(this)
198 , m_matchAuthorAndUserStyles(m_document.settings().authorAndUserStylesEnabled())
199{
200 Element* root = m_document.documentElement();
201
202 CSSDefaultStyleSheets::initDefaultStyle(root);
203
204 // construct document root element default style. this is needed
205 // to evaluate media queries that contain relative constraints, like "screen and (max-width: 10em)"
206 // This is here instead of constructor, because when constructor is run,
207 // document doesn't have documentElement
208 // NOTE: this assumes that element that gets passed to styleForElement -call
209 // is always from the document that owns the style selector
210 FrameView* view = m_document.view();
211 if (view)
212 m_mediaQueryEvaluator = MediaQueryEvaluator { view->mediaType() };
213 else
214 m_mediaQueryEvaluator = MediaQueryEvaluator { "all" };
215
216 if (root) {
217 m_rootDefaultStyle = styleForElement(*root, m_document.renderStyle(), nullptr, RuleMatchingBehavior::MatchOnlyUserAgentRules).renderStyle;
218 // Turn off assertion against font lookups during style resolver initialization. We may need root style font for media queries.
219 m_document.fontSelector().incrementIsComputingRootStyleFont();
220 m_rootDefaultStyle->fontCascade().update(&m_document.fontSelector());
221 m_rootDefaultStyle->fontCascade().primaryFont();
222 m_document.fontSelector().decrementIsComputingRootStyleFont();
223 }
224
225 if (m_rootDefaultStyle && view)
226 m_mediaQueryEvaluator = MediaQueryEvaluator { view->mediaType(), m_document, m_rootDefaultStyle.get() };
227
228 m_ruleSets.resetAuthorStyle();
229 m_ruleSets.resetUserAgentMediaQueryStyle();
230}
231
232void StyleResolver::addCurrentSVGFontFaceRules()
233{
234#if ENABLE(SVG_FONTS)
235 if (m_document.svgExtensions()) {
236 const HashSet<SVGFontFaceElement*>& svgFontFaceElements = m_document.svgExtensions()->svgFontFaceElements();
237 for (auto* svgFontFaceElement : svgFontFaceElements)
238 m_document.fontSelector().addFontFaceRule(svgFontFaceElement->fontFaceRule(), svgFontFaceElement->isInUserAgentShadowTree());
239 }
240#endif
241}
242
243void StyleResolver::appendAuthorStyleSheets(const Vector<RefPtr<CSSStyleSheet>>& styleSheets)
244{
245 m_ruleSets.appendAuthorStyleSheets(styleSheets, &m_mediaQueryEvaluator, m_inspectorCSSOMWrappers, this);
246
247 if (auto renderView = document().renderView())
248 renderView->style().fontCascade().update(&document().fontSelector());
249
250#if ENABLE(CSS_DEVICE_ADAPTATION)
251 viewportStyleResolver()->resolve();
252#endif
253}
254
255// This is a simplified style setting function for keyframe styles
256void StyleResolver::addKeyframeStyle(Ref<StyleRuleKeyframes>&& rule)
257{
258 AtomString s(rule->name());
259 m_keyframesRuleMap.set(s.impl(), WTFMove(rule));
260}
261
262StyleResolver::~StyleResolver()
263{
264 RELEASE_ASSERT(!m_document.isResolvingTreeStyle());
265 RELEASE_ASSERT(!m_isDeleted);
266 m_isDeleted = true;
267
268#if ENABLE(CSS_DEVICE_ADAPTATION)
269 m_viewportStyleResolver->clearDocument();
270#endif
271}
272
273void StyleResolver::sweepMatchedPropertiesCache()
274{
275 // Look for cache entries containing a style declaration with a single ref and remove them.
276 // This may happen when an element attribute mutation causes it to generate a new inlineStyle()
277 // or presentationAttributeStyle(), potentially leaving this cache with the last ref on the old one.
278 Vector<unsigned, 16> toRemove;
279 MatchedPropertiesCache::iterator it = m_matchedPropertiesCache.begin();
280 MatchedPropertiesCache::iterator end = m_matchedPropertiesCache.end();
281 for (; it != end; ++it) {
282 Vector<MatchedProperties>& matchedProperties = it->value.matchedProperties;
283 for (size_t i = 0; i < matchedProperties.size(); ++i) {
284 if (matchedProperties[i].properties->hasOneRef()) {
285 toRemove.append(it->key);
286 break;
287 }
288 }
289 }
290 for (size_t i = 0; i < toRemove.size(); ++i)
291 m_matchedPropertiesCache.remove(toRemove[i]);
292
293 m_matchedPropertiesCacheAdditionsSinceLastSweep = 0;
294}
295
296StyleResolver::State::State(const Element& element, const RenderStyle* parentStyle, const RenderStyle* documentElementStyle, const SelectorFilter* selectorFilter)
297 : m_element(&element)
298 , m_parentStyle(parentStyle)
299 , m_selectorFilter(selectorFilter)
300 , m_elementLinkState(element.document().visitedLinkState().determineLinkState(element))
301{
302 bool resetStyleInheritance = hasShadowRootParent(element) && downcast<ShadowRoot>(element.parentNode())->resetStyleInheritance();
303 if (resetStyleInheritance)
304 m_parentStyle = nullptr;
305
306 auto& document = element.document();
307 auto* documentElement = document.documentElement();
308 if (!documentElement || documentElement == &element)
309 m_rootElementStyle = document.renderStyle();
310 else
311 m_rootElementStyle = documentElementStyle ? documentElementStyle : documentElement->renderStyle();
312
313 updateConversionData();
314}
315
316inline void StyleResolver::State::updateConversionData()
317{
318 m_cssToLengthConversionData = CSSToLengthConversionData(m_style.get(), m_rootElementStyle, m_element ? m_element->document().renderView() : nullptr);
319}
320
321inline void StyleResolver::State::setStyle(std::unique_ptr<RenderStyle> style)
322{
323 m_style = WTFMove(style);
324 updateConversionData();
325}
326
327inline void StyleResolver::State::setParentStyle(std::unique_ptr<RenderStyle> parentStyle)
328{
329 m_ownedParentStyle = WTFMove(parentStyle);
330 m_parentStyle = m_ownedParentStyle.get();
331}
332
333static inline bool isAtShadowBoundary(const Element& element)
334{
335 auto* parentNode = element.parentNode();
336 return parentNode && parentNode->isShadowRoot();
337}
338
339void StyleResolver::setNewStateWithElement(const Element& element)
340{
341 // Apply the declaration to the style. This is a simplified version of the logic in styleForElement.
342 m_state = State(element, nullptr);
343}
344
345ElementStyle StyleResolver::styleForElement(const Element& element, const RenderStyle* parentStyle, const RenderStyle* parentBoxStyle, RuleMatchingBehavior matchingBehavior, const SelectorFilter* selectorFilter)
346{
347 RELEASE_ASSERT(!m_isDeleted);
348
349 m_state = State(element, parentStyle, m_overrideDocumentElementStyle, selectorFilter);
350 State& state = m_state;
351
352 if (state.parentStyle()) {
353 state.setStyle(RenderStyle::createPtr());
354 state.style()->inheritFrom(*state.parentStyle());
355 } else {
356 state.setStyle(defaultStyleForElement());
357 state.setParentStyle(RenderStyle::clonePtr(*state.style()));
358 }
359
360 auto& style = *state.style();
361
362 if (element.isLink()) {
363 style.setIsLink(true);
364 InsideLink linkState = state.elementLinkState();
365 if (linkState != InsideLink::NotInside) {
366 bool forceVisited = InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassVisited);
367 if (forceVisited)
368 linkState = InsideLink::InsideVisited;
369 }
370 style.setInsideLink(linkState);
371 }
372
373 CSSDefaultStyleSheets::ensureDefaultStyleSheetsForElement(element);
374
375 ElementRuleCollector collector(element, m_ruleSets, m_state.selectorFilter());
376 collector.setMedium(&m_mediaQueryEvaluator);
377
378 if (matchingBehavior == RuleMatchingBehavior::MatchOnlyUserAgentRules)
379 collector.matchUARules();
380 else
381 collector.matchAllRules(m_matchAuthorAndUserStyles, matchingBehavior != RuleMatchingBehavior::MatchAllRulesExcludingSMIL);
382
383 if (collector.matchedPseudoElementIds())
384 style.setHasPseudoStyles(collector.matchedPseudoElementIds());
385
386 // This is required for style sharing.
387 if (collector.didMatchUncommonAttributeSelector())
388 style.setUnique();
389
390 auto elementStyleRelations = Style::commitRelationsToRenderStyle(style, element, collector.styleRelations());
391
392 applyMatchedProperties(collector.matchedResult(), element);
393
394 // Clean up our style object's display and text decorations (among other fixups).
395 adjustRenderStyle(*state.style(), *state.parentStyle(), parentBoxStyle, &element);
396
397 if (state.style()->hasViewportUnits())
398 document().setHasStyleWithViewportUnits();
399
400 state.clear(); // Clear out for the next resolve.
401
402 return { state.takeStyle(), WTFMove(elementStyleRelations) };
403}
404
405std::unique_ptr<RenderStyle> StyleResolver::styleForKeyframe(const RenderStyle* elementStyle, const StyleRuleKeyframe* keyframe, KeyframeValue& keyframeValue)
406{
407 RELEASE_ASSERT(!m_isDeleted);
408
409 MatchResult result;
410 result.addMatchedProperties(keyframe->properties());
411
412 ASSERT(!m_state.style());
413
414 State& state = m_state;
415
416 // Create the style
417 state.setStyle(RenderStyle::clonePtr(*elementStyle));
418 state.setParentStyle(RenderStyle::clonePtr(*elementStyle));
419
420 TextDirection direction;
421 WritingMode writingMode;
422 extractDirectionAndWritingMode(*state.style(), result, direction, writingMode);
423
424 // We don't need to bother with !important. Since there is only ever one
425 // decl, there's nothing to override. So just add the first properties.
426 CascadedProperties cascade(direction, writingMode);
427 cascade.addNormalMatches(result, 0, result.matchedProperties().size() - 1);
428
429 ApplyCascadedPropertyState applyState { this, &cascade, &result };
430 applyCascadedProperties(firstCSSProperty, lastHighPriorityProperty, applyState);
431
432 // If our font got dirtied, update it now.
433 updateFont();
434
435 // Now resolve remaining custom properties and the rest, in any order
436 for (auto it = cascade.customProperties().begin(); it != cascade.customProperties().end(); ++it)
437 applyCascadedCustomProperty(it->key, applyState);
438 applyCascadedProperties(firstLowPriorityProperty, lastCSSProperty, applyState);
439
440 // If our font got dirtied by one of the non-essential font props, update it a second time.
441 updateFont();
442
443 cascade.applyDeferredProperties(*this, applyState);
444
445 adjustRenderStyle(*state.style(), *state.parentStyle(), nullptr, nullptr);
446
447 // Add all the animating properties to the keyframe.
448 unsigned propertyCount = keyframe->properties().propertyCount();
449 for (unsigned i = 0; i < propertyCount; ++i) {
450 CSSPropertyID property = keyframe->properties().propertyAt(i).id();
451 // Timing-function within keyframes is special, because it is not animated; it just
452 // describes the timing function between this keyframe and the next.
453 if (property != CSSPropertyAnimationTimingFunction)
454 keyframeValue.addProperty(property);
455 }
456
457 return state.takeStyle();
458}
459
460bool StyleResolver::isAnimationNameValid(const String& name)
461{
462 return m_keyframesRuleMap.find(AtomString(name).impl()) != m_keyframesRuleMap.end();
463}
464
465void StyleResolver::keyframeStylesForAnimation(const Element& element, const RenderStyle* elementStyle, KeyframeList& list)
466{
467 list.clear();
468
469 // Get the keyframesRule for this name.
470 if (list.animationName().isEmpty())
471 return;
472
473 m_keyframesRuleMap.checkConsistency();
474
475 KeyframesRuleMap::iterator it = m_keyframesRuleMap.find(list.animationName().impl());
476 if (it == m_keyframesRuleMap.end())
477 return;
478
479 const StyleRuleKeyframes* keyframesRule = it->value.get();
480
481 auto* keyframes = &keyframesRule->keyframes();
482 Vector<Ref<StyleRuleKeyframe>> newKeyframesIfNecessary;
483
484 bool hasDuplicateKeys = false;
485 HashSet<double> keyframeKeys;
486 for (auto& keyframe : *keyframes) {
487 for (auto key : keyframe->keys()) {
488 if (!keyframeKeys.add(key)) {
489 hasDuplicateKeys = true;
490 break;
491 }
492 }
493 if (hasDuplicateKeys)
494 break;
495 }
496
497 // FIXME: If HashMaps could have Ref<> as value types, we wouldn't need
498 // to copy the HashMap into a Vector.
499 if (hasDuplicateKeys) {
500 // Merge duplicate key times.
501 HashMap<double, RefPtr<StyleRuleKeyframe>> keyframesMap;
502
503 for (auto& originalKeyframe : keyframesRule->keyframes()) {
504 for (auto key : originalKeyframe->keys()) {
505 if (auto keyframe = keyframesMap.get(key))
506 keyframe->mutableProperties().mergeAndOverrideOnConflict(originalKeyframe->properties());
507 else {
508 auto StyleRuleKeyframe = StyleRuleKeyframe::create(MutableStyleProperties::create());
509 StyleRuleKeyframe.ptr()->setKey(key);
510 StyleRuleKeyframe.ptr()->mutableProperties().mergeAndOverrideOnConflict(originalKeyframe->properties());
511 keyframesMap.set(key, StyleRuleKeyframe.ptr());
512 }
513 }
514 }
515
516 for (auto& keyframe : keyframesMap.values())
517 newKeyframesIfNecessary.append(*keyframe.get());
518
519 keyframes = &newKeyframesIfNecessary;
520 }
521
522 // Construct and populate the style for each keyframe.
523 for (auto& keyframe : *keyframes) {
524 setNewStateWithElement(element);
525
526 // Add this keyframe style to all the indicated key times
527 for (auto key : keyframe->keys()) {
528 KeyframeValue keyframeValue(0, nullptr);
529 keyframeValue.setStyle(styleForKeyframe(elementStyle, keyframe.ptr(), keyframeValue));
530 keyframeValue.setKey(key);
531 if (auto timingFunctionCSSValue = keyframe->properties().getPropertyCSSValue(CSSPropertyAnimationTimingFunction))
532 keyframeValue.setTimingFunction(TimingFunction::createFromCSSValue(*timingFunctionCSSValue.get()));
533 list.insert(WTFMove(keyframeValue));
534 }
535 }
536
537 // If the 0% keyframe is missing, create it (but only if there is at least one other keyframe).
538 int initialListSize = list.size();
539 if (initialListSize > 0 && list[0].key()) {
540 static StyleRuleKeyframe* zeroPercentKeyframe;
541 if (!zeroPercentKeyframe) {
542 zeroPercentKeyframe = &StyleRuleKeyframe::create(MutableStyleProperties::create()).leakRef();
543 zeroPercentKeyframe->setKey(0);
544 }
545 KeyframeValue keyframeValue(0, nullptr);
546 keyframeValue.setStyle(styleForKeyframe(elementStyle, zeroPercentKeyframe, keyframeValue));
547 list.insert(WTFMove(keyframeValue));
548 }
549
550 // If the 100% keyframe is missing, create it (but only if there is at least one other keyframe).
551 if (initialListSize > 0 && (list[list.size() - 1].key() != 1)) {
552 static StyleRuleKeyframe* hundredPercentKeyframe;
553 if (!hundredPercentKeyframe) {
554 hundredPercentKeyframe = &StyleRuleKeyframe::create(MutableStyleProperties::create()).leakRef();
555 hundredPercentKeyframe->setKey(1);
556 }
557 KeyframeValue keyframeValue(1, nullptr);
558 keyframeValue.setStyle(styleForKeyframe(elementStyle, hundredPercentKeyframe, keyframeValue));
559 list.insert(WTFMove(keyframeValue));
560 }
561}
562
563std::unique_ptr<RenderStyle> StyleResolver::pseudoStyleForElement(const Element& element, const PseudoStyleRequest& pseudoStyleRequest, const RenderStyle& parentStyle, const SelectorFilter* selectorFilter)
564{
565 m_state = State(element, &parentStyle, m_overrideDocumentElementStyle, selectorFilter);
566
567 State& state = m_state;
568
569 if (m_state.parentStyle()) {
570 state.setStyle(RenderStyle::createPtr());
571 state.style()->inheritFrom(*m_state.parentStyle());
572 } else {
573 state.setStyle(defaultStyleForElement());
574 state.setParentStyle(RenderStyle::clonePtr(*state.style()));
575 }
576
577 // Since we don't use pseudo-elements in any of our quirk/print user agent rules, don't waste time walking
578 // those rules.
579
580 // Check UA, user and author rules.
581 ElementRuleCollector collector(element, m_ruleSets, m_state.selectorFilter());
582 collector.setPseudoStyleRequest(pseudoStyleRequest);
583 collector.setMedium(&m_mediaQueryEvaluator);
584 collector.matchUARules();
585
586 if (m_matchAuthorAndUserStyles) {
587 collector.matchUserRules(false);
588 collector.matchAuthorRules(false);
589 }
590
591 ASSERT(!collector.matchedPseudoElementIds());
592
593 if (collector.matchedResult().matchedProperties().isEmpty())
594 return nullptr;
595
596 state.style()->setStyleType(pseudoStyleRequest.pseudoId);
597
598 applyMatchedProperties(collector.matchedResult(), element);
599
600 // Clean up our style object's display and text decorations (among other fixups).
601 adjustRenderStyle(*state.style(), *m_state.parentStyle(), nullptr, nullptr);
602
603 if (state.style()->hasViewportUnits())
604 document().setHasStyleWithViewportUnits();
605
606 // Now return the style.
607 return state.takeStyle();
608}
609
610std::unique_ptr<RenderStyle> StyleResolver::styleForPage(int pageIndex)
611{
612 RELEASE_ASSERT(!m_isDeleted);
613
614 auto* documentElement = m_document.documentElement();
615 if (!documentElement)
616 return RenderStyle::createPtr();
617
618 m_state = State(*documentElement, m_document.renderStyle());
619
620 m_state.setStyle(RenderStyle::createPtr());
621 m_state.style()->inheritFrom(*m_state.rootElementStyle());
622
623 PageRuleCollector collector(m_state, m_ruleSets);
624 collector.matchAllPageRules(pageIndex);
625
626 MatchResult& result = collector.matchedResult();
627
628 TextDirection direction;
629 WritingMode writingMode;
630 extractDirectionAndWritingMode(*m_state.style(), result, direction, writingMode);
631
632 CascadedProperties cascade(direction, writingMode);
633 cascade.addNormalMatches(result, 0, result.matchedProperties().size() - 1);
634
635 ApplyCascadedPropertyState applyState { this, &cascade, &result };
636 applyCascadedProperties(firstCSSProperty, lastHighPriorityProperty, applyState);
637
638 // If our font got dirtied, update it now.
639 updateFont();
640
641 // Now resolve remaining custom properties and the rest, in any order
642 for (auto it = cascade.customProperties().begin(); it != cascade.customProperties().end(); ++it)
643 applyCascadedCustomProperty(it->key, applyState);
644 applyCascadedProperties(firstLowPriorityProperty, lastCSSProperty, applyState);
645
646 cascade.applyDeferredProperties(*this, applyState);
647
648 // Now return the style.
649 return m_state.takeStyle();
650}
651
652std::unique_ptr<RenderStyle> StyleResolver::defaultStyleForElement()
653{
654 m_state.setStyle(RenderStyle::createPtr());
655 // Make sure our fonts are initialized if we don't inherit them from our parent style.
656 initializeFontStyle();
657 m_state.style()->fontCascade().update(&document().fontSelector());
658 return m_state.takeStyle();
659}
660
661static void addIntrinsicMargins(RenderStyle& style)
662{
663 // Intrinsic margin value.
664 const int intrinsicMargin = clampToInteger(2 * style.effectiveZoom());
665
666 // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
667 // FIXME: Using "hasQuirk" to decide the margin wasn't set is kind of lame.
668 if (style.width().isIntrinsicOrAuto()) {
669 if (style.marginLeft().hasQuirk())
670 style.setMarginLeft(Length(intrinsicMargin, Fixed));
671 if (style.marginRight().hasQuirk())
672 style.setMarginRight(Length(intrinsicMargin, Fixed));
673 }
674
675 if (style.height().isAuto()) {
676 if (style.marginTop().hasQuirk())
677 style.setMarginTop(Length(intrinsicMargin, Fixed));
678 if (style.marginBottom().hasQuirk())
679 style.setMarginBottom(Length(intrinsicMargin, Fixed));
680 }
681}
682
683static DisplayType equivalentBlockDisplay(const RenderStyle& style, const Document& document)
684{
685 switch (auto display = style.display()) {
686 case DisplayType::Block:
687 case DisplayType::Table:
688 case DisplayType::Box:
689 case DisplayType::Flex:
690 case DisplayType::WebKitFlex:
691 case DisplayType::Grid:
692 case DisplayType::FlowRoot:
693 return display;
694
695 case DisplayType::ListItem:
696 // It is a WinIE bug that floated list items lose their bullets, so we'll emulate the quirk, but only in quirks mode.
697 if (document.inQuirksMode() && style.isFloating())
698 return DisplayType::Block;
699 return display;
700 case DisplayType::InlineTable:
701 return DisplayType::Table;
702 case DisplayType::InlineBox:
703 return DisplayType::Box;
704 case DisplayType::InlineFlex:
705 case DisplayType::WebKitInlineFlex:
706 return DisplayType::Flex;
707 case DisplayType::InlineGrid:
708 return DisplayType::Grid;
709
710 case DisplayType::Inline:
711 case DisplayType::Compact:
712 case DisplayType::InlineBlock:
713 case DisplayType::TableRowGroup:
714 case DisplayType::TableHeaderGroup:
715 case DisplayType::TableFooterGroup:
716 case DisplayType::TableRow:
717 case DisplayType::TableColumnGroup:
718 case DisplayType::TableColumn:
719 case DisplayType::TableCell:
720 case DisplayType::TableCaption:
721 return DisplayType::Block;
722 case DisplayType::Contents:
723 ASSERT_NOT_REACHED();
724 return DisplayType::Contents;
725 case DisplayType::None:
726 ASSERT_NOT_REACHED();
727 return DisplayType::None;
728 }
729 ASSERT_NOT_REACHED();
730 return DisplayType::Block;
731}
732
733// CSS requires text-decoration to be reset at each DOM element for tables,
734// inline blocks, inline tables, shadow DOM crossings, floating elements,
735// and absolute or relatively positioned elements.
736static bool doesNotInheritTextDecoration(const RenderStyle& style, const Element* element)
737{
738 return style.display() == DisplayType::Table || style.display() == DisplayType::InlineTable
739 || style.display() == DisplayType::InlineBlock || style.display() == DisplayType::InlineBox || (element && isAtShadowBoundary(*element))
740 || style.isFloating() || style.hasOutOfFlowPosition();
741}
742
743#if ENABLE(OVERFLOW_SCROLLING_TOUCH) || ENABLE(POINTER_EVENTS)
744static bool isScrollableOverflow(Overflow overflow)
745{
746 return overflow == Overflow::Scroll || overflow == Overflow::Auto;
747}
748#endif
749
750void StyleResolver::adjustStyleForInterCharacterRuby()
751{
752 RenderStyle* style = m_state.style();
753 if (style->rubyPosition() != RubyPosition::InterCharacter || !m_state.element() || !m_state.element()->hasTagName(rtTag))
754 return;
755 style->setTextAlign(TextAlignMode::Center);
756 if (style->isHorizontalWritingMode())
757 style->setWritingMode(LeftToRightWritingMode);
758}
759
760static bool hasEffectiveDisplayNoneForDisplayContents(const Element& element)
761{
762 // https://drafts.csswg.org/css-display-3/#unbox-html
763 static NeverDestroyed<HashSet<AtomString>> tagNames = [] {
764 static const HTMLQualifiedName* const tagList[] = {
765 &brTag.get(),
766 &wbrTag.get(),
767 &meterTag.get(),
768 &appletTag.get(),
769 &progressTag.get(),
770 &canvasTag.get(),
771 &embedTag.get(),
772 &objectTag.get(),
773 &audioTag.get(),
774 &iframeTag.get(),
775 &imgTag.get(),
776 &videoTag.get(),
777 &frameTag.get(),
778 &framesetTag.get(),
779 &inputTag.get(),
780 &textareaTag.get(),
781 &selectTag.get(),
782 };
783 HashSet<AtomString> set;
784 for (auto& name : tagList)
785 set.add(name->localName());
786 return set;
787 }();
788
789 // https://drafts.csswg.org/css-display-3/#unbox-svg
790 // FIXME: <g>, <use> and <tspan> have special (?) behavior for display:contents in the current draft spec.
791 if (is<SVGElement>(element))
792 return true;
793#if ENABLE(MATHML)
794 // Not sure MathML code can handle it.
795 if (is<MathMLElement>(element))
796 return true;
797#endif // ENABLE(MATHML)
798 if (!is<HTMLElement>(element))
799 return false;
800 return tagNames.get().contains(element.localName());
801}
802
803static void adjustDisplayContentsStyle(RenderStyle& style, const Element* element)
804{
805 bool displayContentsEnabled = is<HTMLSlotElement>(element) || RuntimeEnabledFeatures::sharedFeatures().displayContentsEnabled();
806 if (!displayContentsEnabled) {
807 style.setDisplay(DisplayType::Inline);
808 return;
809 }
810 if (!element) {
811 if (style.styleType() != PseudoId::Before && style.styleType() != PseudoId::After)
812 style.setDisplay(DisplayType::None);
813 return;
814 }
815 if (element->document().documentElement() == element) {
816 style.setDisplay(DisplayType::Block);
817 return;
818 }
819 if (hasEffectiveDisplayNoneForDisplayContents(*element))
820 style.setDisplay(DisplayType::None);
821}
822
823void StyleResolver::adjustSVGElementStyle(const SVGElement& svgElement, RenderStyle& style)
824{
825 // Only the root <svg> element in an SVG document fragment tree honors css position
826 auto isPositioningAllowed = svgElement.hasTagName(SVGNames::svgTag) && svgElement.parentNode() && !svgElement.parentNode()->isSVGElement() && !svgElement.correspondingElement();
827 if (!isPositioningAllowed)
828 style.setPosition(RenderStyle::initialPosition());
829
830 // RenderSVGRoot handles zooming for the whole SVG subtree, so foreignObject content should
831 // not be scaled again.
832 if (svgElement.hasTagName(SVGNames::foreignObjectTag))
833 style.setEffectiveZoom(RenderStyle::initialZoom());
834
835 // SVG text layout code expects us to be a block-level style element.
836 if ((svgElement.hasTagName(SVGNames::foreignObjectTag) || svgElement.hasTagName(SVGNames::textTag)) && style.isDisplayInlineType())
837 style.setDisplay(DisplayType::Block);
838}
839
840#if ENABLE(POINTER_EVENTS)
841static OptionSet<TouchAction> computeEffectiveTouchActions(const RenderStyle& style, OptionSet<TouchAction> effectiveTouchActions)
842{
843 // https://w3c.github.io/pointerevents/#determining-supported-touch-behavior
844 // "A touch behavior is supported if it conforms to the touch-action property of each element between
845 // the hit tested element and its nearest ancestor with the default touch behavior (including both the
846 // hit tested element and the element with the default touch behavior)."
847
848 bool hasDefaultTouchBehavior = isScrollableOverflow(style.overflowX()) || isScrollableOverflow(style.overflowY());
849 if (hasDefaultTouchBehavior)
850 effectiveTouchActions = RenderStyle::initialTouchActions();
851
852 auto touchActions = style.touchActions();
853 if (touchActions == RenderStyle::initialTouchActions())
854 return effectiveTouchActions;
855
856 if (effectiveTouchActions.contains(TouchAction::None))
857 return { TouchAction::None };
858
859 if (effectiveTouchActions.contains(TouchAction::Auto) || effectiveTouchActions.contains(TouchAction::Manipulation))
860 return touchActions;
861
862 auto sharedTouchActions = effectiveTouchActions & touchActions;
863 if (sharedTouchActions.isEmpty())
864 return { TouchAction::None };
865
866 return sharedTouchActions;
867}
868#endif
869
870#if ENABLE(TEXT_AUTOSIZING)
871static bool hasTextChildren(const Element& element)
872{
873 for (auto* child = element.firstChild(); child; child = child->nextSibling()) {
874 if (is<Text>(child))
875 return true;
876 }
877 return false;
878}
879
880void StyleResolver::adjustRenderStyleForTextAutosizing(RenderStyle& style, const Element& element)
881{
882 auto newAutosizeStatus = AutosizeStatus::updateStatus(style);
883 if (!settings().textAutosizingEnabled() || !settings().textAutosizingUsesIdempotentMode())
884 return;
885
886 if (!hasTextChildren(element))
887 return;
888
889 if (style.textSizeAdjust().isNone())
890 return;
891
892 if (newAutosizeStatus.shouldSkipSubtree())
893 return;
894
895 float initialScale = document().page() ? document().page()->initialScale() : 1;
896 auto fontDescription = style.fontDescription();
897 fontDescription.setComputedSize(AutosizeStatus::idempotentTextSize(fontDescription.specifiedSize(), initialScale));
898 style.setFontDescription(WTFMove(fontDescription));
899 style.fontCascade().update(&document().fontSelector());
900}
901#endif
902
903void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& parentStyle, const RenderStyle* parentBoxStyle, const Element* element)
904{
905 // If the composed tree parent has display:contents, the parent box style will be different from the parent style.
906 // We don't have it when resolving computed style for display:none subtree. Use parent style for adjustments in that case.
907 if (!parentBoxStyle)
908 parentBoxStyle = &parentStyle;
909
910 // Cache our original display.
911 style.setOriginalDisplay(style.display());
912
913 if (style.display() == DisplayType::Contents)
914 adjustDisplayContentsStyle(style, element);
915
916 if (style.display() != DisplayType::None && style.display() != DisplayType::Contents) {
917 if (element) {
918 // If we have a <td> that specifies a float property, in quirks mode we just drop the float
919 // property.
920 // Sites also commonly use display:inline/block on <td>s and <table>s. In quirks mode we force
921 // these tags to retain their display types.
922 if (document().inQuirksMode()) {
923 if (element->hasTagName(tdTag)) {
924 style.setDisplay(DisplayType::TableCell);
925 style.setFloating(Float::No);
926 } else if (is<HTMLTableElement>(*element))
927 style.setDisplay(style.isDisplayInlineType() ? DisplayType::InlineTable : DisplayType::Table);
928 }
929
930 if (element->hasTagName(tdTag) || element->hasTagName(thTag)) {
931 if (style.whiteSpace() == WhiteSpace::KHTMLNoWrap) {
932 // Figure out if we are really nowrapping or if we should just
933 // use normal instead. If the width of the cell is fixed, then
934 // we don't actually use WhiteSpace::NoWrap.
935 if (style.width().isFixed())
936 style.setWhiteSpace(WhiteSpace::Normal);
937 else
938 style.setWhiteSpace(WhiteSpace::NoWrap);
939 }
940 }
941
942 // Tables never support the -webkit-* values for text-align and will reset back to the default.
943 if (is<HTMLTableElement>(*element) && (style.textAlign() == TextAlignMode::WebKitLeft || style.textAlign() == TextAlignMode::WebKitCenter || style.textAlign() == TextAlignMode::WebKitRight))
944 style.setTextAlign(TextAlignMode::Start);
945
946 // Frames and framesets never honor position:relative or position:absolute. This is necessary to
947 // fix a crash where a site tries to position these objects. They also never honor display.
948 if (element->hasTagName(frameTag) || element->hasTagName(framesetTag)) {
949 style.setPosition(PositionType::Static);
950 style.setDisplay(DisplayType::Block);
951 }
952
953 // Ruby text does not support float or position. This might change with evolution of the specification.
954 if (element->hasTagName(rtTag)) {
955 style.setPosition(PositionType::Static);
956 style.setFloating(Float::No);
957 }
958
959 // User agents are expected to have a rule in their user agent stylesheet that matches th elements that have a parent
960 // node whose computed value for the 'text-align' property is its initial value, whose declaration block consists of
961 // just a single declaration that sets the 'text-align' property to the value 'center'.
962 // https://html.spec.whatwg.org/multipage/rendering.html#rendering
963 if (element->hasTagName(thTag) && !style.hasExplicitlySetTextAlign() && parentStyle.textAlign() == RenderStyle::initialTextAlign())
964 style.setTextAlign(TextAlignMode::Center);
965
966 if (element->hasTagName(legendTag))
967 style.setDisplay(DisplayType::Block);
968 }
969
970 // Absolute/fixed positioned elements, floating elements and the document element need block-like outside display.
971 if (style.hasOutOfFlowPosition() || style.isFloating() || (element && element->document().documentElement() == element))
972 style.setDisplay(equivalentBlockDisplay(style, document()));
973
974 // FIXME: Don't support this mutation for pseudo styles like first-letter or first-line, since it's not completely
975 // clear how that should work.
976 if (style.display() == DisplayType::Inline && style.styleType() == PseudoId::None && style.writingMode() != parentStyle.writingMode())
977 style.setDisplay(DisplayType::InlineBlock);
978
979 // After performing the display mutation, check table rows. We do not honor position:relative or position:sticky on
980 // table rows or cells. This has been established for position:relative in CSS2.1 (and caused a crash in containingBlock()
981 // on some sites).
982 if ((style.display() == DisplayType::TableHeaderGroup || style.display() == DisplayType::TableRowGroup
983 || style.display() == DisplayType::TableFooterGroup || style.display() == DisplayType::TableRow)
984 && style.position() == PositionType::Relative)
985 style.setPosition(PositionType::Static);
986
987 // writing-mode does not apply to table row groups, table column groups, table rows, and table columns.
988 // FIXME: Table cells should be allowed to be perpendicular or flipped with respect to the table, though.
989 if (style.display() == DisplayType::TableColumn || style.display() == DisplayType::TableColumnGroup || style.display() == DisplayType::TableFooterGroup
990 || style.display() == DisplayType::TableHeaderGroup || style.display() == DisplayType::TableRow || style.display() == DisplayType::TableRowGroup
991 || style.display() == DisplayType::TableCell)
992 style.setWritingMode(parentStyle.writingMode());
993
994 // FIXME: Since we don't support block-flow on flexible boxes yet, disallow setting
995 // of block-flow to anything other than TopToBottomWritingMode.
996 // https://bugs.webkit.org/show_bug.cgi?id=46418 - Flexible box support.
997 if (style.writingMode() != TopToBottomWritingMode && (style.display() == DisplayType::Box || style.display() == DisplayType::InlineBox))
998 style.setWritingMode(TopToBottomWritingMode);
999
1000 // https://www.w3.org/TR/css-display/#transformations
1001 // "A parent with a grid or flex display value blockifies the box’s display type."
1002 if (parentBoxStyle->isDisplayFlexibleOrGridBox()) {
1003 style.setFloating(Float::No);
1004 style.setDisplay(equivalentBlockDisplay(style, document()));
1005 }
1006 }
1007
1008 // Make sure our z-index value is only applied if the object is positioned.
1009 if (style.position() == PositionType::Static && !parentBoxStyle->isDisplayFlexibleOrGridBox())
1010 style.setHasAutoZIndex();
1011
1012 // Auto z-index becomes 0 for the root element and transparent objects. This prevents
1013 // cases where objects that should be blended as a single unit end up with a non-transparent
1014 // object wedged in between them. Auto z-index also becomes 0 for objects that specify transforms/masks/reflections.
1015 if (style.hasAutoZIndex()) {
1016 if ((element && element->document().documentElement() == element)
1017 || style.opacity() < 1.0f
1018 || style.hasTransformRelatedProperty()
1019 || style.hasMask()
1020 || style.clipPath()
1021 || style.boxReflect()
1022 || style.hasFilter()
1023#if ENABLE(FILTERS_LEVEL_2)
1024 || style.hasBackdropFilter()
1025#endif
1026 || style.hasBlendMode()
1027 || style.hasIsolation()
1028 || style.position() == PositionType::Sticky
1029 || style.position() == PositionType::Fixed
1030 || style.willChangeCreatesStackingContext())
1031 style.setZIndex(0);
1032 }
1033
1034 if (element) {
1035 // Textarea considers overflow visible as auto.
1036 if (is<HTMLTextAreaElement>(*element)) {
1037 style.setOverflowX(style.overflowX() == Overflow::Visible ? Overflow::Auto : style.overflowX());
1038 style.setOverflowY(style.overflowY() == Overflow::Visible ? Overflow::Auto : style.overflowY());
1039 }
1040
1041 // Disallow -webkit-user-modify on :pseudo and ::pseudo elements.
1042 if (!element->shadowPseudoId().isNull())
1043 style.setUserModify(UserModify::ReadOnly);
1044
1045 if (is<HTMLMarqueeElement>(*element)) {
1046 // For now, <marquee> requires an overflow clip to work properly.
1047 style.setOverflowX(Overflow::Hidden);
1048 style.setOverflowY(Overflow::Hidden);
1049
1050 bool isVertical = style.marqueeDirection() == MarqueeDirection::Up || style.marqueeDirection() == MarqueeDirection::Down;
1051 // Make horizontal marquees not wrap.
1052 if (!isVertical) {
1053 style.setWhiteSpace(WhiteSpace::NoWrap);
1054 style.setTextAlign(TextAlignMode::Start);
1055 }
1056 // Apparently this is the expected legacy behavior.
1057 if (isVertical && style.height().isAuto())
1058 style.setHeight(Length(200, Fixed));
1059 }
1060 }
1061
1062 if (doesNotInheritTextDecoration(style, element))
1063 style.setTextDecorationsInEffect(style.textDecoration());
1064 else
1065 style.addToTextDecorationsInEffect(style.textDecoration());
1066
1067 // If either overflow value is not visible, change to auto.
1068 if (style.overflowX() == Overflow::Visible && style.overflowY() != Overflow::Visible) {
1069 // FIXME: Once we implement pagination controls, overflow-x should default to hidden
1070 // if overflow-y is set to -webkit-paged-x or -webkit-page-y. For now, we'll let it
1071 // default to auto so we can at least scroll through the pages.
1072 style.setOverflowX(Overflow::Auto);
1073 } else if (style.overflowY() == Overflow::Visible && style.overflowX() != Overflow::Visible)
1074 style.setOverflowY(Overflow::Auto);
1075
1076 // Call setStylesForPaginationMode() if a pagination mode is set for any non-root elements. If these
1077 // styles are specified on a root element, then they will be incorporated in
1078 // Style::createForDocument().
1079 if ((style.overflowY() == Overflow::PagedX || style.overflowY() == Overflow::PagedY) && !(element && (element->hasTagName(htmlTag) || element->hasTagName(bodyTag))))
1080 style.setColumnStylesFromPaginationMode(WebCore::paginationModeForRenderStyle(style));
1081
1082 // Table rows, sections and the table itself will support overflow:hidden and will ignore scroll/auto.
1083 // FIXME: Eventually table sections will support auto and scroll.
1084 if (style.display() == DisplayType::Table || style.display() == DisplayType::InlineTable
1085 || style.display() == DisplayType::TableRowGroup || style.display() == DisplayType::TableRow) {
1086 if (style.overflowX() != Overflow::Visible && style.overflowX() != Overflow::Hidden)
1087 style.setOverflowX(Overflow::Visible);
1088 if (style.overflowY() != Overflow::Visible && style.overflowY() != Overflow::Hidden)
1089 style.setOverflowY(Overflow::Visible);
1090 }
1091
1092 // Menulists should have visible overflow
1093 if (style.appearance() == MenulistPart) {
1094 style.setOverflowX(Overflow::Visible);
1095 style.setOverflowY(Overflow::Visible);
1096 }
1097
1098#if ENABLE(OVERFLOW_SCROLLING_TOUCH)
1099 // Touch overflow scrolling creates a stacking context.
1100 if (style.hasAutoZIndex() && style.useTouchOverflowScrolling() && (isScrollableOverflow(style.overflowX()) || isScrollableOverflow(style.overflowY())))
1101 style.setZIndex(0);
1102#endif
1103
1104 // Cull out any useless layers and also repeat patterns into additional layers.
1105 style.adjustBackgroundLayers();
1106 style.adjustMaskLayers();
1107
1108 // Do the same for animations and transitions.
1109 style.adjustAnimations();
1110 style.adjustTransitions();
1111
1112 // Important: Intrinsic margins get added to controls before the theme has adjusted the style, since the theme will
1113 // alter fonts and heights/widths.
1114 if (is<HTMLFormControlElement>(element) && style.computedFontPixelSize() >= 11) {
1115 // Don't apply intrinsic margins to image buttons. The designer knows how big the images are,
1116 // so we have to treat all image buttons as though they were explicitly sized.
1117 if (!is<HTMLInputElement>(*element) || !downcast<HTMLInputElement>(*element).isImageButton())
1118 addIntrinsicMargins(style);
1119 }
1120
1121 // Let the theme also have a crack at adjusting the style.
1122 if (style.hasAppearance())
1123 RenderTheme::singleton().adjustStyle(*this, style, element, m_state.hasUAAppearance(), m_state.borderData(), m_state.backgroundData(), m_state.backgroundColor());
1124
1125 // If we have first-letter pseudo style, do not share this style.
1126 if (style.hasPseudoStyle(PseudoId::FirstLetter))
1127 style.setUnique();
1128
1129 // FIXME: when dropping the -webkit prefix on transform-style, we should also have opacity < 1 cause flattening.
1130 if (style.preserves3D() && (style.overflowX() != Overflow::Visible
1131 || style.overflowY() != Overflow::Visible
1132 || style.hasClip()
1133 || style.clipPath()
1134 || style.hasFilter()
1135#if ENABLE(FILTERS_LEVEL_2)
1136 || style.hasBackdropFilter()
1137#endif
1138 || style.hasBlendMode()))
1139 style.setTransformStyle3D(TransformStyle3D::Flat);
1140
1141 if (is<SVGElement>(element))
1142 adjustSVGElementStyle(downcast<SVGElement>(*element), style);
1143
1144 // If the inherited value of justify-items includes the 'legacy' keyword (plus 'left', 'right' or
1145 // 'center'), 'legacy' computes to the the inherited value. Otherwise, 'auto' computes to 'normal'.
1146 if (parentBoxStyle->justifyItems().positionType() == ItemPositionType::Legacy && style.justifyItems().position() == ItemPosition::Legacy)
1147 style.setJustifyItems(parentBoxStyle->justifyItems());
1148
1149#if ENABLE(POINTER_EVENTS)
1150 style.setEffectiveTouchActions(computeEffectiveTouchActions(style, parentStyle.effectiveTouchActions()));
1151#endif
1152
1153 if (element) {
1154#if ENABLE(TEXT_AUTOSIZING)
1155 adjustRenderStyleForTextAutosizing(style, *element);
1156#endif
1157 adjustRenderStyleForSiteSpecificQuirks(style, *element);
1158 }
1159}
1160
1161void StyleResolver::adjustRenderStyleForSiteSpecificQuirks(RenderStyle& style, const Element& element)
1162{
1163 if (document().quirks().needsGMailOverflowScrollQuirk()) {
1164 // This turns sidebar scrollable without mouse move event.
1165 static NeverDestroyed<AtomString> roleValue("navigation", AtomString::ConstructFromLiteral);
1166 if (style.overflowY() == Overflow::Hidden && element.attributeWithoutSynchronization(roleAttr) == roleValue)
1167 style.setOverflowY(Overflow::Auto);
1168 }
1169 if (document().quirks().needsYouTubeOverflowScrollQuirk()) {
1170 // This turns sidebar scrollable without hover.
1171 static NeverDestroyed<AtomString> idValue("guide-inner-content", AtomString::ConstructFromLiteral);
1172 if (style.overflowY() == Overflow::Hidden && element.idForStyleResolution() == idValue)
1173 style.setOverflowY(Overflow::Auto);
1174 }
1175}
1176
1177static void checkForOrientationChange(RenderStyle* style)
1178{
1179 FontOrientation fontOrientation;
1180 NonCJKGlyphOrientation glyphOrientation;
1181 std::tie(fontOrientation, glyphOrientation) = style->fontAndGlyphOrientation();
1182
1183 const auto& fontDescription = style->fontDescription();
1184 if (fontDescription.orientation() == fontOrientation && fontDescription.nonCJKGlyphOrientation() == glyphOrientation)
1185 return;
1186
1187 auto newFontDescription = fontDescription;
1188 newFontDescription.setNonCJKGlyphOrientation(glyphOrientation);
1189 newFontDescription.setOrientation(fontOrientation);
1190 style->setFontDescription(WTFMove(newFontDescription));
1191}
1192
1193void StyleResolver::updateFont()
1194{
1195 if (!m_state.fontDirty())
1196 return;
1197
1198 RenderStyle* style = m_state.style();
1199#if ENABLE(TEXT_AUTOSIZING)
1200 checkForTextSizeAdjust(style);
1201#endif
1202 checkForGenericFamilyChange(style, m_state.parentStyle());
1203 checkForZoomChange(style, m_state.parentStyle());
1204 checkForOrientationChange(style);
1205 style->fontCascade().update(&document().fontSelector());
1206 if (m_state.fontSizeHasViewportUnits())
1207 style->setHasViewportUnits(true);
1208 m_state.setFontDirty(false);
1209}
1210
1211Vector<RefPtr<StyleRule>> StyleResolver::styleRulesForElement(const Element* element, unsigned rulesToInclude)
1212{
1213 return pseudoStyleRulesForElement(element, PseudoId::None, rulesToInclude);
1214}
1215
1216Vector<RefPtr<StyleRule>> StyleResolver::pseudoStyleRulesForElement(const Element* element, PseudoId pseudoId, unsigned rulesToInclude)
1217{
1218 if (!element)
1219 return { };
1220
1221 m_state = State(*element, nullptr);
1222
1223 ElementRuleCollector collector(*element, m_ruleSets, m_state.selectorFilter());
1224 collector.setMode(SelectorChecker::Mode::CollectingRules);
1225 collector.setPseudoStyleRequest(PseudoStyleRequest(pseudoId));
1226 collector.setMedium(&m_mediaQueryEvaluator);
1227
1228 if (rulesToInclude & UAAndUserCSSRules) {
1229 // First we match rules from the user agent sheet.
1230 collector.matchUARules();
1231
1232 // Now we check user sheet rules.
1233 if (m_matchAuthorAndUserStyles)
1234 collector.matchUserRules(rulesToInclude & EmptyCSSRules);
1235 }
1236
1237 if (m_matchAuthorAndUserStyles && (rulesToInclude & AuthorCSSRules))
1238 collector.matchAuthorRules(rulesToInclude & EmptyCSSRules);
1239
1240 return collector.matchedRuleList();
1241}
1242
1243static bool shouldApplyPropertyInParseOrder(CSSPropertyID propertyID)
1244{
1245 switch (propertyID) {
1246 case CSSPropertyWebkitBackgroundClip:
1247 case CSSPropertyBackgroundClip:
1248 case CSSPropertyWebkitBackgroundOrigin:
1249 case CSSPropertyBackgroundOrigin:
1250 case CSSPropertyWebkitBackgroundSize:
1251 case CSSPropertyBackgroundSize:
1252 case CSSPropertyWebkitBorderImage:
1253 case CSSPropertyBorderImage:
1254 case CSSPropertyBorderImageSlice:
1255 case CSSPropertyBorderImageSource:
1256 case CSSPropertyBorderImageOutset:
1257 case CSSPropertyBorderImageRepeat:
1258 case CSSPropertyBorderImageWidth:
1259 case CSSPropertyWebkitBoxShadow:
1260 case CSSPropertyBoxShadow:
1261 case CSSPropertyWebkitTextDecoration:
1262 case CSSPropertyTextDecorationLine:
1263 case CSSPropertyTextDecorationStyle:
1264 case CSSPropertyTextDecorationColor:
1265 case CSSPropertyTextDecorationSkip:
1266 case CSSPropertyTextUnderlinePosition:
1267 case CSSPropertyTextUnderlineOffset:
1268 case CSSPropertyTextDecorationThickness:
1269 case CSSPropertyTextDecoration:
1270 return true;
1271 default:
1272 return false;
1273 }
1274}
1275
1276static bool elementTypeHasAppearanceFromUAStyle(const Element& element)
1277{
1278 // NOTE: This is just a hard-coded list of elements that have some -webkit-appearance value in html.css
1279 const auto& localName = element.localName();
1280 return localName == HTMLNames::inputTag
1281 || localName == HTMLNames::textareaTag
1282 || localName == HTMLNames::buttonTag
1283 || localName == HTMLNames::progressTag
1284 || localName == HTMLNames::selectTag
1285 || localName == HTMLNames::meterTag;
1286}
1287
1288unsigned StyleResolver::computeMatchedPropertiesHash(const MatchedProperties* properties, unsigned size)
1289{
1290 return StringHasher::hashMemory(properties, sizeof(MatchedProperties) * size);
1291}
1292
1293bool operator==(const StyleResolver::MatchRanges& a, const StyleResolver::MatchRanges& b)
1294{
1295 return a.firstUARule == b.firstUARule
1296 && a.lastUARule == b.lastUARule
1297 && a.firstAuthorRule == b.firstAuthorRule
1298 && a.lastAuthorRule == b.lastAuthorRule
1299 && a.firstUserRule == b.firstUserRule
1300 && a.lastUserRule == b.lastUserRule;
1301}
1302
1303bool operator!=(const StyleResolver::MatchRanges& a, const StyleResolver::MatchRanges& b)
1304{
1305 return !(a == b);
1306}
1307
1308bool operator==(const StyleResolver::MatchedProperties& a, const StyleResolver::MatchedProperties& b)
1309{
1310 return a.properties == b.properties && a.linkMatchType == b.linkMatchType;
1311}
1312
1313bool operator!=(const StyleResolver::MatchedProperties& a, const StyleResolver::MatchedProperties& b)
1314{
1315 return !(a == b);
1316}
1317
1318const StyleResolver::MatchedPropertiesCacheItem* StyleResolver::findFromMatchedPropertiesCache(unsigned hash, const MatchResult& matchResult)
1319{
1320 ASSERT(hash);
1321
1322 MatchedPropertiesCache::iterator it = m_matchedPropertiesCache.find(hash);
1323 if (it == m_matchedPropertiesCache.end())
1324 return nullptr;
1325 MatchedPropertiesCacheItem& cacheItem = it->value;
1326
1327 size_t size = matchResult.matchedProperties().size();
1328 if (size != cacheItem.matchedProperties.size())
1329 return nullptr;
1330 for (size_t i = 0; i < size; ++i) {
1331 if (matchResult.matchedProperties()[i] != cacheItem.matchedProperties[i])
1332 return nullptr;
1333 }
1334 if (cacheItem.ranges != matchResult.ranges)
1335 return nullptr;
1336 return &cacheItem;
1337}
1338
1339void StyleResolver::addToMatchedPropertiesCache(const RenderStyle* style, const RenderStyle* parentStyle, unsigned hash, const MatchResult& matchResult)
1340{
1341 static const unsigned matchedDeclarationCacheAdditionsBetweenSweeps = 100;
1342 if (++m_matchedPropertiesCacheAdditionsSinceLastSweep >= matchedDeclarationCacheAdditionsBetweenSweeps
1343 && !m_matchedPropertiesCacheSweepTimer.isActive()) {
1344 static const Seconds matchedDeclarationCacheSweepTime { 1_min };
1345 m_matchedPropertiesCacheSweepTimer.startOneShot(matchedDeclarationCacheSweepTime);
1346 }
1347
1348 ASSERT(hash);
1349 // Note that we don't cache the original RenderStyle instance. It may be further modified.
1350 // The RenderStyle in the cache is really just a holder for the substructures and never used as-is.
1351 MatchedPropertiesCacheItem cacheItem(matchResult, style, parentStyle);
1352 m_matchedPropertiesCache.add(hash, WTFMove(cacheItem));
1353}
1354
1355void StyleResolver::invalidateMatchedPropertiesCache()
1356{
1357 m_matchedPropertiesCache.clear();
1358}
1359
1360void StyleResolver::clearCachedPropertiesAffectedByViewportUnits()
1361{
1362 Vector<unsigned, 16> toRemove;
1363 for (auto& cacheKeyValue : m_matchedPropertiesCache) {
1364 if (cacheKeyValue.value.renderStyle->hasViewportUnits())
1365 toRemove.append(cacheKeyValue.key);
1366 }
1367 for (auto key : toRemove)
1368 m_matchedPropertiesCache.remove(key);
1369}
1370
1371static bool isCacheableInMatchedPropertiesCache(const Element& element, const RenderStyle* style, const RenderStyle* parentStyle)
1372{
1373 // FIXME: Writing mode and direction properties modify state when applying to document element by calling
1374 // Document::setWritingMode/DirectionSetOnDocumentElement. We can't skip the applying by caching.
1375 if (&element == element.document().documentElement())
1376 return false;
1377 // content:attr() value depends on the element it is being applied to.
1378 if (style->hasAttrContent() || (style->styleType() != PseudoId::None && parentStyle->hasAttrContent()))
1379 return false;
1380 if (style->hasAppearance())
1381 return false;
1382 if (style->zoom() != RenderStyle::initialZoom())
1383 return false;
1384 if (style->writingMode() != RenderStyle::initialWritingMode() || style->direction() != RenderStyle::initialDirection())
1385 return false;
1386 // The cache assumes static knowledge about which properties are inherited.
1387 if (style->hasExplicitlyInheritedProperties())
1388 return false;
1389 return true;
1390}
1391
1392void extractDirectionAndWritingMode(const RenderStyle& style, const StyleResolver::MatchResult& matchResult, TextDirection& direction, WritingMode& writingMode)
1393{
1394 direction = style.direction();
1395 writingMode = style.writingMode();
1396
1397 bool hadImportantWritingMode = false;
1398 bool hadImportantDirection = false;
1399
1400 for (const auto& matchedProperties : matchResult.matchedProperties()) {
1401 for (unsigned i = 0, count = matchedProperties.properties->propertyCount(); i < count; ++i) {
1402 auto property = matchedProperties.properties->propertyAt(i);
1403 if (!property.value()->isPrimitiveValue())
1404 continue;
1405 switch (property.id()) {
1406 case CSSPropertyWritingMode:
1407 if (!hadImportantWritingMode || property.isImportant()) {
1408 writingMode = downcast<CSSPrimitiveValue>(*property.value());
1409 hadImportantWritingMode = property.isImportant();
1410 }
1411 break;
1412 case CSSPropertyDirection:
1413 if (!hadImportantDirection || property.isImportant()) {
1414 direction = downcast<CSSPrimitiveValue>(*property.value());
1415 hadImportantDirection = property.isImportant();
1416 }
1417 break;
1418 default:
1419 break;
1420 }
1421 }
1422 }
1423}
1424
1425void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const Element& element, ShouldUseMatchedPropertiesCache shouldUseMatchedPropertiesCache)
1426{
1427 State& state = m_state;
1428 unsigned cacheHash = shouldUseMatchedPropertiesCache && matchResult.isCacheable ? computeMatchedPropertiesHash(matchResult.matchedProperties().data(), matchResult.matchedProperties().size()) : 0;
1429 bool applyInheritedOnly = false;
1430 const MatchedPropertiesCacheItem* cacheItem = nullptr;
1431 if (cacheHash && (cacheItem = findFromMatchedPropertiesCache(cacheHash, matchResult))
1432 && isCacheableInMatchedPropertiesCache(element, state.style(), state.parentStyle())) {
1433 // We can build up the style by copying non-inherited properties from an earlier style object built using the same exact
1434 // style declarations. We then only need to apply the inherited properties, if any, as their values can depend on the
1435 // element context. This is fast and saves memory by reusing the style data structures.
1436 state.style()->copyNonInheritedFrom(*cacheItem->renderStyle);
1437 if (state.parentStyle()->inheritedDataShared(cacheItem->parentRenderStyle.get()) && !isAtShadowBoundary(element)) {
1438 InsideLink linkStatus = state.style()->insideLink();
1439 // If the cache item parent style has identical inherited properties to the current parent style then the
1440 // resulting style will be identical too. We copy the inherited properties over from the cache and are done.
1441 state.style()->inheritFrom(*cacheItem->renderStyle);
1442
1443 // Unfortunately the link status is treated like an inherited property. We need to explicitly restore it.
1444 state.style()->setInsideLink(linkStatus);
1445 return;
1446 }
1447 applyInheritedOnly = true;
1448 }
1449
1450 // Directional properties (*-before/after) are aliases that depend on the TextDirection and WritingMode.
1451 // These must be resolved before we can begin the property cascade.
1452 TextDirection direction;
1453 WritingMode writingMode;
1454 extractDirectionAndWritingMode(*state.style(), matchResult, direction, writingMode);
1455
1456 if (elementTypeHasAppearanceFromUAStyle(*state.element())) {
1457 // FIXME: This is such a hack.
1458 // Find out if there's a -webkit-appearance property in effect from the UA sheet.
1459 // If so, we cache the border and background styles so that RenderTheme::adjustStyle()
1460 // can look at them later to figure out if this is a styled form control or not.
1461 CascadedProperties cascade(direction, writingMode);
1462 cascade.addNormalMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly);
1463 cascade.addImportantMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly);
1464
1465 ApplyCascadedPropertyState applyState { this, &cascade, &matchResult };
1466
1467 applyCascadedProperties(CSSPropertyWebkitRubyPosition, CSSPropertyWebkitRubyPosition, applyState);
1468 adjustStyleForInterCharacterRuby();
1469
1470#if ENABLE(DARK_MODE_CSS)
1471 // Supported color schemes can affect resolved colors, so we need to apply that property before any color properties.
1472 applyCascadedProperties(CSSPropertyColorScheme, CSSPropertyColorScheme, applyState);
1473#endif
1474
1475 applyCascadedProperties(firstCSSProperty, lastHighPriorityProperty, applyState);
1476
1477 // If our font got dirtied, update it now.
1478 updateFont();
1479
1480 // Now resolve remaining custom properties and the rest, in any order
1481 for (auto it = cascade.customProperties().begin(); it != cascade.customProperties().end(); ++it)
1482 applyCascadedCustomProperty(it->key, applyState);
1483 applyCascadedProperties(firstLowPriorityProperty, lastCSSProperty, applyState);
1484
1485 state.cacheBorderAndBackground();
1486 }
1487
1488 CascadedProperties cascade(direction, writingMode);
1489 cascade.addNormalMatches(matchResult, 0, matchResult.matchedProperties().size() - 1, applyInheritedOnly);
1490 cascade.addImportantMatches(matchResult, matchResult.ranges.firstAuthorRule, matchResult.ranges.lastAuthorRule, applyInheritedOnly);
1491 cascade.addImportantMatches(matchResult, matchResult.ranges.firstUserRule, matchResult.ranges.lastUserRule, applyInheritedOnly);
1492 cascade.addImportantMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly);
1493
1494 ApplyCascadedPropertyState applyState { this, &cascade, &matchResult };
1495
1496 applyCascadedProperties(CSSPropertyWebkitRubyPosition, CSSPropertyWebkitRubyPosition, applyState);
1497 adjustStyleForInterCharacterRuby();
1498
1499#if ENABLE(DARK_MODE_CSS)
1500 // Supported color schemes can affect resolved colors, so we need to apply that property before any color properties.
1501 applyCascadedProperties(CSSPropertyColorScheme, CSSPropertyColorScheme, applyState);
1502#endif
1503
1504 applyCascadedProperties(firstCSSProperty, lastHighPriorityProperty, applyState);
1505
1506 // If the effective zoom value changes, we can't use the matched properties cache. Start over.
1507 if (cacheItem && cacheItem->renderStyle->effectiveZoom() != state.style()->effectiveZoom())
1508 return applyMatchedProperties(matchResult, element, DoNotUseMatchedPropertiesCache);
1509
1510 // If our font got dirtied, update it now.
1511 updateFont();
1512
1513 // If the font changed, we can't use the matched properties cache. Start over.
1514 if (cacheItem && cacheItem->renderStyle->fontDescription() != state.style()->fontDescription())
1515 return applyMatchedProperties(matchResult, element, DoNotUseMatchedPropertiesCache);
1516
1517 // Now resolve remaining custom properties and the rest, in any order
1518 for (auto it = cascade.customProperties().begin(); it != cascade.customProperties().end(); ++it)
1519 applyCascadedCustomProperty(it->key, applyState);
1520 applyCascadedProperties(firstLowPriorityProperty, lastCSSProperty, applyState);
1521
1522 // Finally, some properties must be applied in the order they were parsed.
1523 // There are some CSS properties that affect the same RenderStyle values,
1524 // so to preserve behavior, we queue them up during cascade and flush here.
1525 cascade.applyDeferredProperties(*this, applyState);
1526
1527 ASSERT(!state.fontDirty());
1528
1529 if (cacheItem || !cacheHash)
1530 return;
1531 if (!isCacheableInMatchedPropertiesCache(*state.element(), state.style(), state.parentStyle()))
1532 return;
1533 addToMatchedPropertiesCache(state.style(), state.parentStyle(), cacheHash, matchResult);
1534}
1535
1536void StyleResolver::applyPropertyToStyle(CSSPropertyID id, CSSValue* value, std::unique_ptr<RenderStyle> style)
1537{
1538 m_state = State();
1539 m_state.setParentStyle(RenderStyle::clonePtr(*style));
1540 m_state.setStyle(WTFMove(style));
1541 applyPropertyToCurrentStyle(id, value);
1542}
1543
1544void StyleResolver::applyPropertyToCurrentStyle(CSSPropertyID id, CSSValue* value)
1545{
1546 ApplyCascadedPropertyState applyState { this, nullptr, nullptr };
1547 if (value)
1548 applyProperty(id, value, applyState);
1549}
1550
1551inline bool isValidVisitedLinkProperty(CSSPropertyID id)
1552{
1553 switch (id) {
1554 case CSSPropertyBackgroundColor:
1555 case CSSPropertyBorderLeftColor:
1556 case CSSPropertyBorderRightColor:
1557 case CSSPropertyBorderTopColor:
1558 case CSSPropertyBorderBottomColor:
1559 case CSSPropertyCaretColor:
1560 case CSSPropertyColor:
1561 case CSSPropertyOutlineColor:
1562 case CSSPropertyColumnRuleColor:
1563 case CSSPropertyTextDecorationColor:
1564 case CSSPropertyWebkitTextEmphasisColor:
1565 case CSSPropertyWebkitTextFillColor:
1566 case CSSPropertyWebkitTextStrokeColor:
1567 case CSSPropertyFill:
1568 case CSSPropertyStroke:
1569 case CSSPropertyStrokeColor:
1570 return true;
1571 default:
1572 break;
1573 }
1574
1575 return false;
1576}
1577
1578// https://www.w3.org/TR/css-pseudo-4/#marker-pseudo (Editor's Draft, 25 July 2017)
1579static inline bool isValidMarkerStyleProperty(CSSPropertyID id)
1580{
1581 switch (id) {
1582 case CSSPropertyColor:
1583 case CSSPropertyFontFamily:
1584 case CSSPropertyFontFeatureSettings:
1585 case CSSPropertyFontSize:
1586 case CSSPropertyFontStretch:
1587 case CSSPropertyFontStyle:
1588 case CSSPropertyFontSynthesis:
1589 case CSSPropertyFontVariantAlternates:
1590 case CSSPropertyFontVariantCaps:
1591 case CSSPropertyFontVariantEastAsian:
1592 case CSSPropertyFontVariantLigatures:
1593 case CSSPropertyFontVariantNumeric:
1594 case CSSPropertyFontVariantPosition:
1595 case CSSPropertyFontWeight:
1596#if ENABLE(VARIATION_FONTS)
1597 case CSSPropertyFontOpticalSizing:
1598 case CSSPropertyFontVariationSettings:
1599#endif
1600 return true;
1601 default:
1602 break;
1603 }
1604 return false;
1605}
1606
1607#if ENABLE(VIDEO_TRACK)
1608static inline bool isValidCueStyleProperty(CSSPropertyID id)
1609{
1610 switch (id) {
1611 case CSSPropertyBackground:
1612 case CSSPropertyBackgroundAttachment:
1613 case CSSPropertyBackgroundClip:
1614 case CSSPropertyBackgroundColor:
1615 case CSSPropertyBackgroundImage:
1616 case CSSPropertyBackgroundOrigin:
1617 case CSSPropertyBackgroundPosition:
1618 case CSSPropertyBackgroundPositionX:
1619 case CSSPropertyBackgroundPositionY:
1620 case CSSPropertyBackgroundRepeat:
1621 case CSSPropertyBackgroundSize:
1622 case CSSPropertyColor:
1623 case CSSPropertyFont:
1624 case CSSPropertyFontFamily:
1625 case CSSPropertyFontSize:
1626 case CSSPropertyFontStyle:
1627 case CSSPropertyFontVariantCaps:
1628 case CSSPropertyFontWeight:
1629 case CSSPropertyLineHeight:
1630 case CSSPropertyOpacity:
1631 case CSSPropertyOutline:
1632 case CSSPropertyOutlineColor:
1633 case CSSPropertyOutlineOffset:
1634 case CSSPropertyOutlineStyle:
1635 case CSSPropertyOutlineWidth:
1636 case CSSPropertyVisibility:
1637 case CSSPropertyWhiteSpace:
1638 case CSSPropertyTextDecoration:
1639 case CSSPropertyTextShadow:
1640 case CSSPropertyBorderStyle:
1641 case CSSPropertyPaintOrder:
1642 case CSSPropertyStrokeLinejoin:
1643 case CSSPropertyStrokeLinecap:
1644 case CSSPropertyStrokeColor:
1645 case CSSPropertyStrokeWidth:
1646 return true;
1647 default:
1648 break;
1649 }
1650 return false;
1651}
1652#endif
1653// SVG handles zooming in a different way compared to CSS. The whole document is scaled instead
1654// of each individual length value in the render style / tree. CSSPrimitiveValue::computeLength*()
1655// multiplies each resolved length with the zoom multiplier - so for SVG we need to disable that.
1656// Though all CSS values that can be applied to outermost <svg> elements (width/height/border/padding...)
1657// need to respect the scaling. RenderBox (the parent class of RenderSVGRoot) grabs values like
1658// width/height/border/padding/... from the RenderStyle -> for SVG these values would never scale,
1659// if we'd pass a 1.0 zoom factor everyhwere. So we only pass a zoom factor of 1.0 for specific
1660// properties that are NOT allowed to scale within a zoomed SVG document (letter/word-spacing/font-size).
1661bool StyleResolver::useSVGZoomRules() const
1662{
1663 return m_state.element() && m_state.element()->isSVGElement();
1664}
1665
1666// Scale with/height properties on inline SVG root.
1667bool StyleResolver::useSVGZoomRulesForLength() const
1668{
1669 return is<SVGElement>(m_state.element()) && !(is<SVGSVGElement>(*m_state.element()) && m_state.element()->parentNode());
1670}
1671
1672StyleResolver::CascadedProperties* StyleResolver::cascadedPropertiesForRollback(const MatchResult& matchResult)
1673{
1674 ASSERT(cascadeLevel() != CascadeLevel::UserAgentLevel);
1675
1676 TextDirection direction;
1677 WritingMode writingMode;
1678 extractDirectionAndWritingMode(*state().style(), matchResult, direction, writingMode);
1679
1680 if (cascadeLevel() == CascadeLevel::AuthorLevel) {
1681 CascadedProperties* authorRollback = state().authorRollback();
1682 if (authorRollback)
1683 return authorRollback;
1684
1685 auto newAuthorRollback(std::make_unique<CascadedProperties>(direction, writingMode));
1686
1687 // This special rollback cascade contains UA rules and user rules but no author rules.
1688 newAuthorRollback->addNormalMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, false);
1689 newAuthorRollback->addNormalMatches(matchResult, matchResult.ranges.firstUserRule, matchResult.ranges.lastUserRule, false);
1690 newAuthorRollback->addImportantMatches(matchResult, matchResult.ranges.firstUserRule, matchResult.ranges.lastUserRule, false);
1691 newAuthorRollback->addImportantMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, false);
1692
1693 state().setAuthorRollback(newAuthorRollback);
1694 return state().authorRollback();
1695 }
1696
1697 if (cascadeLevel() == CascadeLevel::UserLevel) {
1698 CascadedProperties* userRollback = state().userRollback();
1699 if (userRollback)
1700 return userRollback;
1701
1702 auto newUserRollback(std::make_unique<CascadedProperties>(direction, writingMode));
1703
1704 // This special rollback cascade contains only UA rules.
1705 newUserRollback->addNormalMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, false);
1706 newUserRollback->addImportantMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, false);
1707
1708 state().setUserRollback(newUserRollback);
1709 return state().userRollback();
1710 }
1711
1712 return nullptr;
1713}
1714
1715void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, ApplyCascadedPropertyState& applyState, SelectorChecker::LinkMatchMask linkMatchMask)
1716{
1717 auto* matchResult = applyState.matchResult;
1718 ASSERT_WITH_MESSAGE(!isShorthandCSSProperty(id), "Shorthand property id = %d wasn't expanded at parsing time", id);
1719
1720 State& state = m_state;
1721
1722 RefPtr<CSSValue> valueToApply = value;
1723 if (value->hasVariableReferences()) {
1724 valueToApply = resolvedVariableValue(id, *value, applyState);
1725 // If appliedProperties already has this id, then we detected a cycle, and this value should be unset.
1726 if (!valueToApply || applyState.appliedProperties.get(id)) {
1727 if (CSSProperty::isInheritedProperty(id))
1728 valueToApply = CSSValuePool::singleton().createInheritedValue();
1729 else
1730 valueToApply = CSSValuePool::singleton().createExplicitInitialValue();
1731 }
1732 }
1733
1734 if (CSSProperty::isDirectionAwareProperty(id)) {
1735 CSSPropertyID newId = CSSProperty::resolveDirectionAwareProperty(id, state.style()->direction(), state.style()->writingMode());
1736 ASSERT(newId != id);
1737 return applyProperty(newId, valueToApply.get(), applyState, linkMatchMask);
1738 }
1739
1740 CSSValue* valueToCheckForInheritInitial = valueToApply.get();
1741 CSSCustomPropertyValue* customPropertyValue = nullptr;
1742 CSSValueID customPropertyValueID = CSSValueInvalid;
1743
1744 CSSRegisteredCustomProperty* customPropertyRegistered = nullptr;
1745
1746 if (id == CSSPropertyCustom) {
1747 customPropertyValue = &downcast<CSSCustomPropertyValue>(*valueToApply);
1748 ASSERT(customPropertyValue->isResolved());
1749 if (WTF::holds_alternative<CSSValueID>(customPropertyValue->value()))
1750 customPropertyValueID = WTF::get<CSSValueID>(customPropertyValue->value());
1751 auto& name = customPropertyValue->name();
1752 customPropertyRegistered = document().getCSSRegisteredCustomPropertySet().get(name);
1753 }
1754
1755 bool isInherit = state.parentStyle() ? valueToCheckForInheritInitial->isInheritedValue() || customPropertyValueID == CSSValueInherit : false;
1756 bool isInitial = valueToCheckForInheritInitial->isInitialValue() || customPropertyValueID == CSSValueInitial || (!state.parentStyle() && (valueToCheckForInheritInitial->isInheritedValue() || customPropertyValueID == CSSValueInherit));
1757
1758 bool isUnset = valueToCheckForInheritInitial->isUnsetValue() || customPropertyValueID == CSSValueUnset;
1759 bool isRevert = valueToCheckForInheritInitial->isRevertValue() || customPropertyValueID == CSSValueRevert;
1760
1761 if (isRevert) {
1762 if (cascadeLevel() == CascadeLevel::UserAgentLevel || !matchResult)
1763 isUnset = true;
1764 else {
1765 // Fetch the correct rollback object from the state, building it if necessary.
1766 // This requires having the original MatchResult available.
1767 auto* rollback = cascadedPropertiesForRollback(*matchResult);
1768 ASSERT(rollback);
1769
1770 // With the cascade built, we need to obtain the property and apply it. If the property is
1771 // not present, then we behave like "unset." Otherwise we apply the property instead of
1772 // our own.
1773 if (customPropertyValue) {
1774 if (customPropertyRegistered && customPropertyRegistered->inherits && rollback->hasCustomProperty(customPropertyValue->name())) {
1775 auto property = rollback->customProperty(customPropertyValue->name());
1776 if (property.cssValue[linkMatchMask])
1777 applyProperty(property.id, property.cssValue[linkMatchMask], applyState, linkMatchMask);
1778 return;
1779 }
1780 } else if (rollback->hasProperty(id)) {
1781 auto& property = rollback->property(id);
1782 if (property.cssValue[linkMatchMask])
1783 applyProperty(property.id, property.cssValue[linkMatchMask], applyState, linkMatchMask);
1784 return;
1785 }
1786
1787 isUnset = true;
1788 }
1789 }
1790
1791 if (isUnset) {
1792 if (CSSProperty::isInheritedProperty(id))
1793 isInherit = true;
1794 else
1795 isInitial = true;
1796 }
1797
1798 ASSERT(!isInherit || !isInitial); // isInherit -> !isInitial && isInitial -> !isInherit
1799
1800 if (!state.applyPropertyToRegularStyle() && (!state.applyPropertyToVisitedLinkStyle() || !isValidVisitedLinkProperty(id))) {
1801 // Limit the properties that can be applied to only the ones honored by :visited.
1802 return;
1803 }
1804
1805 if (isInherit && !CSSProperty::isInheritedProperty(id))
1806 state.style()->setHasExplicitlyInheritedProperties();
1807
1808#if ENABLE(CSS_PAINTING_API)
1809 if (is<CSSPaintImageValue>(*valueToApply)) {
1810 auto& name = downcast<CSSPaintImageValue>(*valueToApply).name();
1811 if (auto* paintWorklet = document().paintWorkletGlobalScopeForName(name)) {
1812 auto locker = holdLock(paintWorklet->paintDefinitionLock());
1813 if (auto* registration = paintWorklet->paintDefinitionMap().get(name)) {
1814 for (auto& property : registration->inputProperties)
1815 state.style()->addCustomPaintWatchProperty(property);
1816 }
1817 }
1818 }
1819#endif
1820
1821 // Use the generated StyleBuilder.
1822 StyleBuilder::applyProperty(id, *this, *valueToApply, isInitial, isInherit, customPropertyRegistered);
1823}
1824
1825RefPtr<CSSValue> StyleResolver::resolvedVariableValue(CSSPropertyID propID, const CSSValue& value, ApplyCascadedPropertyState& state) const
1826{
1827 CSSParser parser(document());
1828 return parser.parseValueWithVariableReferences(propID, value, state);
1829}
1830
1831RefPtr<StyleImage> StyleResolver::styleImage(CSSValue& value)
1832{
1833 if (is<CSSImageGeneratorValue>(value)) {
1834 if (is<CSSGradientValue>(value))
1835 return StyleGeneratedImage::create(downcast<CSSGradientValue>(value).gradientWithStylesResolved(*this));
1836
1837 if (is<CSSFilterImageValue>(value)) {
1838 // FilterImage needs to calculate FilterOperations.
1839 downcast<CSSFilterImageValue>(value).createFilterOperations(this);
1840 }
1841 return StyleGeneratedImage::create(downcast<CSSImageGeneratorValue>(value));
1842 }
1843
1844 if (is<CSSImageValue>(value) || is<CSSImageSetValue>(value) || is<CSSCursorImageValue>(value))
1845 return StyleCachedImage::create(value);
1846
1847 return nullptr;
1848}
1849
1850#if ENABLE(TEXT_AUTOSIZING)
1851void StyleResolver::checkForTextSizeAdjust(RenderStyle* style)
1852{
1853 ASSERT(style);
1854 if (style->textSizeAdjust().isAuto() || (settings().textAutosizingUsesIdempotentMode() && !style->textSizeAdjust().isNone()))
1855 return;
1856
1857 auto newFontDescription = style->fontDescription();
1858 if (!style->textSizeAdjust().isNone())
1859 newFontDescription.setComputedSize(newFontDescription.specifiedSize() * style->textSizeAdjust().multiplier());
1860 else
1861 newFontDescription.setComputedSize(newFontDescription.specifiedSize());
1862 style->setFontDescription(WTFMove(newFontDescription));
1863}
1864#endif
1865
1866void StyleResolver::checkForZoomChange(RenderStyle* style, const RenderStyle* parentStyle)
1867{
1868 if (!parentStyle)
1869 return;
1870
1871 if (style->effectiveZoom() == parentStyle->effectiveZoom() && style->textZoom() == parentStyle->textZoom())
1872 return;
1873
1874 const auto& childFont = style->fontDescription();
1875 auto newFontDescription = childFont;
1876 setFontSize(newFontDescription, childFont.specifiedSize());
1877 style->setFontDescription(WTFMove(newFontDescription));
1878}
1879
1880void StyleResolver::checkForGenericFamilyChange(RenderStyle* style, const RenderStyle* parentStyle)
1881{
1882 const auto& childFont = style->fontDescription();
1883
1884 if (childFont.isAbsoluteSize() || !parentStyle)
1885 return;
1886
1887 const auto& parentFont = parentStyle->fontDescription();
1888 if (childFont.useFixedDefaultSize() == parentFont.useFixedDefaultSize())
1889 return;
1890 // We know the parent is monospace or the child is monospace, and that font
1891 // size was unspecified. We want to scale our font size as appropriate.
1892 // If the font uses a keyword size, then we refetch from the table rather than
1893 // multiplying by our scale factor.
1894 float size;
1895 if (CSSValueID sizeIdentifier = childFont.keywordSizeAsIdentifier())
1896 size = Style::fontSizeForKeyword(sizeIdentifier, childFont.useFixedDefaultSize(), document());
1897 else {
1898 float fixedScaleFactor = (settings().defaultFixedFontSize() && settings().defaultFontSize())
1899 ? static_cast<float>(settings().defaultFixedFontSize()) / settings().defaultFontSize()
1900 : 1;
1901 size = parentFont.useFixedDefaultSize() ?
1902 childFont.specifiedSize() / fixedScaleFactor :
1903 childFont.specifiedSize() * fixedScaleFactor;
1904 }
1905
1906 auto newFontDescription = childFont;
1907 setFontSize(newFontDescription, size);
1908 style->setFontDescription(WTFMove(newFontDescription));
1909}
1910
1911void StyleResolver::initializeFontStyle()
1912{
1913 FontCascadeDescription fontDescription;
1914 fontDescription.setRenderingMode(settings().fontRenderingMode());
1915 fontDescription.setOneFamily(standardFamily);
1916 fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium);
1917 setFontSize(fontDescription, Style::fontSizeForKeyword(CSSValueMedium, false, document()));
1918 fontDescription.setShouldAllowUserInstalledFonts(settings().shouldAllowUserInstalledFonts() ? AllowUserInstalledFonts::Yes : AllowUserInstalledFonts::No);
1919 setFontDescription(WTFMove(fontDescription));
1920}
1921
1922void StyleResolver::setFontSize(FontCascadeDescription& fontDescription, float size)
1923{
1924 fontDescription.setSpecifiedSize(size);
1925 fontDescription.setComputedSize(Style::computedFontSizeFromSpecifiedSize(size, fontDescription.isAbsoluteSize(), useSVGZoomRules(), m_state.style(), document()));
1926}
1927
1928bool StyleResolver::colorFromPrimitiveValueIsDerivedFromElement(const CSSPrimitiveValue& value)
1929{
1930 switch (value.valueID()) {
1931 case CSSValueWebkitText:
1932 case CSSValueWebkitLink:
1933 case CSSValueWebkitActivelink:
1934 case CSSValueCurrentcolor:
1935 return true;
1936 default:
1937 return false;
1938 }
1939}
1940
1941Color StyleResolver::colorFromPrimitiveValue(const CSSPrimitiveValue& value, bool forVisitedLink) const
1942{
1943 if (value.isRGBColor())
1944 return value.color();
1945
1946 auto identifier = value.valueID();
1947 switch (identifier) {
1948 case CSSValueWebkitText:
1949 return document().textColor();
1950 case CSSValueWebkitLink:
1951 return (m_state.element()->isLink() && forVisitedLink) ? document().visitedLinkColor() : document().linkColor();
1952 case CSSValueWebkitActivelink:
1953 return document().activeLinkColor();
1954 case CSSValueWebkitFocusRingColor:
1955 return RenderTheme::singleton().focusRingColor(document().styleColorOptions(m_state.style()));
1956 case CSSValueCurrentcolor:
1957 // Color is an inherited property so depending on it effectively makes the property inherited.
1958 // FIXME: Setting the flag as a side effect of calling this function is a bit oblique. Can we do better?
1959 m_state.style()->setHasExplicitlyInheritedProperties();
1960 return m_state.style()->color();
1961 default:
1962 return StyleColor::colorFromKeyword(identifier, document().styleColorOptions(m_state.style()));
1963 }
1964}
1965
1966void StyleResolver::addViewportDependentMediaQueryResult(const MediaQueryExpression& expression, bool result)
1967{
1968 m_viewportDependentMediaQueryResults.append(MediaQueryResult { expression, result });
1969}
1970
1971bool StyleResolver::hasMediaQueriesAffectedByViewportChange() const
1972{
1973 LOG(MediaQueries, "StyleResolver::hasMediaQueriesAffectedByViewportChange evaluating queries");
1974 for (auto& result : m_viewportDependentMediaQueryResults) {
1975 if (m_mediaQueryEvaluator.evaluate(result.expression) != result.result)
1976 return true;
1977 }
1978 return false;
1979}
1980
1981void StyleResolver::addAccessibilitySettingsDependentMediaQueryResult(const MediaQueryExpression& expression, bool result)
1982{
1983 m_accessibilitySettingsDependentMediaQueryResults.append(MediaQueryResult { expression, result });
1984}
1985
1986bool StyleResolver::hasMediaQueriesAffectedByAccessibilitySettingsChange() const
1987{
1988 LOG(MediaQueries, "StyleResolver::hasMediaQueriesAffectedByAccessibilitySettingsChange evaluating queries");
1989 for (auto& result : m_accessibilitySettingsDependentMediaQueryResults) {
1990 if (m_mediaQueryEvaluator.evaluate(result.expression) != result.result)
1991 return true;
1992 }
1993 return false;
1994}
1995
1996void StyleResolver::addAppearanceDependentMediaQueryResult(const MediaQueryExpression& expression, bool result)
1997{
1998 m_appearanceDependentMediaQueryResults.append(MediaQueryResult { expression, result });
1999}
2000
2001bool StyleResolver::hasMediaQueriesAffectedByAppearanceChange() const
2002{
2003 LOG(MediaQueries, "StyleResolver::hasMediaQueriesAffectedByAppearanceChange evaluating queries");
2004 for (auto& result : m_appearanceDependentMediaQueryResults) {
2005 if (m_mediaQueryEvaluator.evaluate(result.expression) != result.result)
2006 return true;
2007 }
2008 return false;
2009}
2010
2011static FilterOperation::OperationType filterOperationForType(CSSValueID type)
2012{
2013 switch (type) {
2014 case CSSValueUrl:
2015 return FilterOperation::REFERENCE;
2016 case CSSValueGrayscale:
2017 return FilterOperation::GRAYSCALE;
2018 case CSSValueSepia:
2019 return FilterOperation::SEPIA;
2020 case CSSValueSaturate:
2021 return FilterOperation::SATURATE;
2022 case CSSValueHueRotate:
2023 return FilterOperation::HUE_ROTATE;
2024 case CSSValueInvert:
2025 return FilterOperation::INVERT;
2026 case CSSValueAppleInvertLightness:
2027 return FilterOperation::APPLE_INVERT_LIGHTNESS;
2028 case CSSValueOpacity:
2029 return FilterOperation::OPACITY;
2030 case CSSValueBrightness:
2031 return FilterOperation::BRIGHTNESS;
2032 case CSSValueContrast:
2033 return FilterOperation::CONTRAST;
2034 case CSSValueBlur:
2035 return FilterOperation::BLUR;
2036 case CSSValueDropShadow:
2037 return FilterOperation::DROP_SHADOW;
2038 default:
2039 break;
2040 }
2041 ASSERT_NOT_REACHED();
2042 return FilterOperation::NONE;
2043}
2044
2045bool StyleResolver::createFilterOperations(const CSSValue& inValue, FilterOperations& outOperations)
2046{
2047 State& state = m_state;
2048 ASSERT(outOperations.isEmpty());
2049
2050 if (is<CSSPrimitiveValue>(inValue)) {
2051 auto& primitiveValue = downcast<CSSPrimitiveValue>(inValue);
2052 if (primitiveValue.valueID() == CSSValueNone)
2053 return true;
2054 }
2055
2056 if (!is<CSSValueList>(inValue))
2057 return false;
2058
2059 FilterOperations operations;
2060 for (auto& currentValue : downcast<CSSValueList>(inValue)) {
2061
2062 if (is<CSSPrimitiveValue>(currentValue)) {
2063 auto& primitiveValue = downcast<CSSPrimitiveValue>(currentValue.get());
2064 if (!primitiveValue.isURI())
2065 continue;
2066
2067 String cssUrl = primitiveValue.stringValue();
2068 URL url = document().completeURL(cssUrl);
2069
2070 auto operation = ReferenceFilterOperation::create(cssUrl, url.fragmentIdentifier());
2071 operations.operations().append(WTFMove(operation));
2072 continue;
2073 }
2074
2075 if (!is<CSSFunctionValue>(currentValue))
2076 continue;
2077
2078 auto& filterValue = downcast<CSSFunctionValue>(currentValue.get());
2079 FilterOperation::OperationType operationType = filterOperationForType(filterValue.name());
2080
2081 // Check that all parameters are primitive values, with the
2082 // exception of drop shadow which has a CSSShadowValue parameter.
2083 const CSSPrimitiveValue* firstValue = nullptr;
2084 if (operationType != FilterOperation::DROP_SHADOW) {
2085 bool haveNonPrimitiveValue = false;
2086 for (unsigned j = 0; j < filterValue.length(); ++j) {
2087 if (!is<CSSPrimitiveValue>(*filterValue.itemWithoutBoundsCheck(j))) {
2088 haveNonPrimitiveValue = true;
2089 break;
2090 }
2091 }
2092 if (haveNonPrimitiveValue)
2093 continue;
2094 if (filterValue.length())
2095 firstValue = downcast<CSSPrimitiveValue>(filterValue.itemWithoutBoundsCheck(0));
2096 }
2097
2098 switch (operationType) {
2099 case FilterOperation::GRAYSCALE:
2100 case FilterOperation::SEPIA:
2101 case FilterOperation::SATURATE: {
2102 double amount = 1;
2103 if (filterValue.length() == 1) {
2104 amount = firstValue->doubleValue();
2105 if (firstValue->isPercentage())
2106 amount /= 100;
2107 }
2108
2109 operations.operations().append(BasicColorMatrixFilterOperation::create(amount, operationType));
2110 break;
2111 }
2112 case FilterOperation::HUE_ROTATE: {
2113 double angle = 0;
2114 if (filterValue.length() == 1)
2115 angle = firstValue->computeDegrees();
2116
2117 operations.operations().append(BasicColorMatrixFilterOperation::create(angle, operationType));
2118 break;
2119 }
2120 case FilterOperation::INVERT:
2121 case FilterOperation::BRIGHTNESS:
2122 case FilterOperation::CONTRAST:
2123 case FilterOperation::OPACITY: {
2124 double amount = 1;
2125 if (filterValue.length() == 1) {
2126 amount = firstValue->doubleValue();
2127 if (firstValue->isPercentage())
2128 amount /= 100;
2129 }
2130
2131 operations.operations().append(BasicComponentTransferFilterOperation::create(amount, operationType));
2132 break;
2133 }
2134 case FilterOperation::APPLE_INVERT_LIGHTNESS: {
2135 operations.operations().append(InvertLightnessFilterOperation::create());
2136 break;
2137 }
2138 case FilterOperation::BLUR: {
2139 Length stdDeviation = Length(0, Fixed);
2140 if (filterValue.length() >= 1)
2141 stdDeviation = convertToFloatLength(firstValue, state.cssToLengthConversionData());
2142 if (stdDeviation.isUndefined())
2143 return false;
2144
2145 operations.operations().append(BlurFilterOperation::create(stdDeviation));
2146 break;
2147 }
2148 case FilterOperation::DROP_SHADOW: {
2149 if (filterValue.length() != 1)
2150 return false;
2151
2152 const auto* cssValue = filterValue.itemWithoutBoundsCheck(0);
2153 if (!is<CSSShadowValue>(cssValue))
2154 continue;
2155
2156 const auto& item = downcast<CSSShadowValue>(*cssValue);
2157 int x = item.x->computeLength<int>(state.cssToLengthConversionData());
2158 int y = item.y->computeLength<int>(state.cssToLengthConversionData());
2159 IntPoint location(x, y);
2160 int blur = item.blur ? item.blur->computeLength<int>(state.cssToLengthConversionData()) : 0;
2161 Color color;
2162 if (item.color)
2163 color = colorFromPrimitiveValue(*item.color);
2164
2165 operations.operations().append(DropShadowFilterOperation::create(location, blur, color.isValid() ? color : Color::transparent));
2166 break;
2167 }
2168 default:
2169 ASSERT_NOT_REACHED();
2170 break;
2171 }
2172 }
2173
2174 outOperations = operations;
2175 return true;
2176}
2177
2178inline StyleResolver::MatchedProperties::MatchedProperties() = default;
2179
2180StyleResolver::MatchedProperties::~MatchedProperties() = default;
2181
2182StyleResolver::CascadedProperties::CascadedProperties(TextDirection direction, WritingMode writingMode)
2183 : m_direction(direction)
2184 , m_writingMode(writingMode)
2185{
2186}
2187
2188inline bool StyleResolver::CascadedProperties::hasProperty(CSSPropertyID id) const
2189{
2190 ASSERT(id < m_propertyIsPresent.size());
2191 return m_propertyIsPresent[id];
2192}
2193
2194inline StyleResolver::CascadedProperties::Property& StyleResolver::CascadedProperties::property(CSSPropertyID id)
2195{
2196 return m_properties[id];
2197}
2198
2199inline bool StyleResolver::CascadedProperties::hasCustomProperty(const String& name) const
2200{
2201 return m_customProperties.contains(name);
2202}
2203
2204inline StyleResolver::CascadedProperties::Property StyleResolver::CascadedProperties::customProperty(const String& name) const
2205{
2206 return m_customProperties.get(name);
2207}
2208
2209void StyleResolver::CascadedProperties::setPropertyInternal(Property& property, CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel, Style::ScopeOrdinal styleScopeOrdinal)
2210{
2211 ASSERT(linkMatchType <= SelectorChecker::MatchAll);
2212 property.id = id;
2213 property.level = cascadeLevel;
2214 property.styleScopeOrdinal = styleScopeOrdinal;
2215 if (linkMatchType == SelectorChecker::MatchAll) {
2216 property.cssValue[0] = &cssValue;
2217 property.cssValue[SelectorChecker::MatchLink] = &cssValue;
2218 property.cssValue[SelectorChecker::MatchVisited] = &cssValue;
2219 } else
2220 property.cssValue[linkMatchType] = &cssValue;
2221}
2222
2223void StyleResolver::CascadedProperties::set(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel, Style::ScopeOrdinal styleScopeOrdinal)
2224{
2225 if (CSSProperty::isDirectionAwareProperty(id))
2226 id = CSSProperty::resolveDirectionAwareProperty(id, m_direction, m_writingMode);
2227
2228 ASSERT(!shouldApplyPropertyInParseOrder(id));
2229
2230 auto& property = m_properties[id];
2231 ASSERT(id < m_propertyIsPresent.size());
2232 if (id == CSSPropertyCustom) {
2233 m_propertyIsPresent.set(id);
2234 const auto& customValue = downcast<CSSCustomPropertyValue>(cssValue);
2235 bool hasValue = customProperties().contains(customValue.name());
2236 if (!hasValue) {
2237 Property property;
2238 property.id = id;
2239 memset(property.cssValue, 0, sizeof(property.cssValue));
2240 setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
2241 customProperties().set(customValue.name(), property);
2242 } else {
2243 Property property = customProperties().get(customValue.name());
2244 setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
2245 customProperties().set(customValue.name(), property);
2246 }
2247 return;
2248 }
2249
2250 if (!m_propertyIsPresent[id])
2251 memset(property.cssValue, 0, sizeof(property.cssValue));
2252 m_propertyIsPresent.set(id);
2253 setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
2254}
2255
2256void StyleResolver::CascadedProperties::setDeferred(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel, Style::ScopeOrdinal styleScopeOrdinal)
2257{
2258 ASSERT(!CSSProperty::isDirectionAwareProperty(id));
2259 ASSERT(shouldApplyPropertyInParseOrder(id));
2260
2261 Property property;
2262 memset(property.cssValue, 0, sizeof(property.cssValue));
2263 setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
2264 m_deferredProperties.append(property);
2265}
2266
2267static CascadeLevel cascadeLevelForIndex(const StyleResolver::MatchResult& matchResult, int index)
2268{
2269 if (index >= matchResult.ranges.firstUARule && index <= matchResult.ranges.lastUARule)
2270 return CascadeLevel::UserAgentLevel;
2271 if (index >= matchResult.ranges.firstUserRule && index <= matchResult.ranges.lastUserRule)
2272 return CascadeLevel::UserLevel;
2273 return CascadeLevel::AuthorLevel;
2274}
2275
2276void StyleResolver::CascadedProperties::addMatch(const MatchResult& matchResult, unsigned index, bool isImportant, bool inheritedOnly)
2277{
2278 auto& matchedProperties = matchResult.matchedProperties()[index];
2279 auto& styleProperties = *matchedProperties.properties;
2280
2281 auto propertyWhitelistType = static_cast<PropertyWhitelistType>(matchedProperties.whitelistType);
2282 auto cascadeLevel = cascadeLevelForIndex(matchResult, index);
2283
2284 for (unsigned i = 0, count = styleProperties.propertyCount(); i < count; ++i) {
2285 auto current = styleProperties.propertyAt(i);
2286 if (isImportant != current.isImportant())
2287 continue;
2288 if (inheritedOnly && !current.isInherited()) {
2289 // We apply the inherited properties only when using the property cache.
2290 // A match with a value that is explicitely inherited should never have been cached.
2291 ASSERT(!current.value()->isInheritedValue());
2292 continue;
2293 }
2294 CSSPropertyID propertyID = current.id();
2295
2296#if ENABLE(VIDEO_TRACK)
2297 if (propertyWhitelistType == PropertyWhitelistCue && !isValidCueStyleProperty(propertyID))
2298 continue;
2299#endif
2300 if (propertyWhitelistType == PropertyWhitelistMarker && !isValidMarkerStyleProperty(propertyID))
2301 continue;
2302
2303 if (shouldApplyPropertyInParseOrder(propertyID))
2304 setDeferred(propertyID, *current.value(), matchedProperties.linkMatchType, cascadeLevel, matchedProperties.styleScopeOrdinal);
2305 else
2306 set(propertyID, *current.value(), matchedProperties.linkMatchType, cascadeLevel, matchedProperties.styleScopeOrdinal);
2307 }
2308}
2309
2310void StyleResolver::CascadedProperties::addNormalMatches(const MatchResult& matchResult, int startIndex, int endIndex, bool inheritedOnly)
2311{
2312 if (startIndex == -1)
2313 return;
2314
2315 for (int i = startIndex; i <= endIndex; ++i)
2316 addMatch(matchResult, i, false, inheritedOnly);
2317}
2318
2319static bool hasImportantProperties(const StyleProperties& properties)
2320{
2321 for (unsigned i = 0, count = properties.propertyCount(); i < count; ++i) {
2322 if (properties.propertyAt(i).isImportant())
2323 return true;
2324 }
2325 return false;
2326}
2327
2328void StyleResolver::CascadedProperties::addImportantMatches(const MatchResult& matchResult, int startIndex, int endIndex, bool inheritedOnly)
2329{
2330 if (startIndex == -1)
2331 return;
2332
2333 struct IndexAndOrdinal {
2334 int index;
2335 Style::ScopeOrdinal ordinal;
2336 };
2337 Vector<IndexAndOrdinal> shadowTreeMatches;
2338
2339 for (int i = startIndex; i <= endIndex; ++i) {
2340 const MatchedProperties& matchedProperties = matchResult.matchedProperties()[i];
2341
2342 if (!hasImportantProperties(*matchedProperties.properties))
2343 continue;
2344
2345 if (matchedProperties.styleScopeOrdinal != Style::ScopeOrdinal::Element) {
2346 shadowTreeMatches.append({ i, matchedProperties.styleScopeOrdinal });
2347 continue;
2348 }
2349
2350 addMatch(matchResult, i, true, inheritedOnly);
2351 }
2352
2353 if (shadowTreeMatches.isEmpty())
2354 return;
2355
2356 // For !important properties a later shadow tree wins.
2357 // Match results are sorted in reverse tree context order so this is not needed for normal properties.
2358 std::stable_sort(shadowTreeMatches.begin(), shadowTreeMatches.end(), [] (const IndexAndOrdinal& a, const IndexAndOrdinal& b) {
2359 return a.ordinal < b.ordinal;
2360 });
2361
2362 for (auto& match : shadowTreeMatches)
2363 addMatch(matchResult, match.index, true, inheritedOnly);
2364}
2365
2366void StyleResolver::CascadedProperties::applyDeferredProperties(StyleResolver& resolver, ApplyCascadedPropertyState& applyState)
2367{
2368 for (auto& property : m_deferredProperties)
2369 property.apply(resolver, applyState);
2370}
2371
2372void StyleResolver::CascadedProperties::Property::apply(StyleResolver& resolver, ApplyCascadedPropertyState& applyState)
2373{
2374 State& state = resolver.state();
2375 state.setCascadeLevel(level);
2376 state.setStyleScopeOrdinal(styleScopeOrdinal);
2377
2378 if (cssValue[SelectorChecker::MatchDefault]) {
2379 state.setApplyPropertyToRegularStyle(true);
2380 state.setApplyPropertyToVisitedLinkStyle(false);
2381 resolver.applyProperty(id, cssValue[SelectorChecker::MatchDefault], applyState, SelectorChecker::MatchDefault);
2382 }
2383
2384 if (state.style()->insideLink() == InsideLink::NotInside)
2385 return;
2386
2387 if (cssValue[SelectorChecker::MatchLink]) {
2388 state.setApplyPropertyToRegularStyle(true);
2389 state.setApplyPropertyToVisitedLinkStyle(false);
2390 resolver.applyProperty(id, cssValue[SelectorChecker::MatchLink], applyState, SelectorChecker::MatchLink);
2391 }
2392
2393 if (cssValue[SelectorChecker::MatchVisited]) {
2394 state.setApplyPropertyToRegularStyle(false);
2395 state.setApplyPropertyToVisitedLinkStyle(true);
2396 resolver.applyProperty(id, cssValue[SelectorChecker::MatchVisited], applyState, SelectorChecker::MatchVisited);
2397 }
2398
2399 state.setApplyPropertyToRegularStyle(true);
2400 state.setApplyPropertyToVisitedLinkStyle(false);
2401}
2402
2403void StyleResolver::applyCascadedCustomProperty(const String& name, ApplyCascadedPropertyState& state)
2404{
2405 if (state.appliedCustomProperties.contains(name) || !state.cascade->customProperties().contains(name))
2406 return;
2407
2408 auto property = state.cascade->customProperties().get(name);
2409 bool inCycle = state.inProgressPropertiesCustom.contains(name);
2410
2411 for (auto index : { SelectorChecker::MatchDefault, SelectorChecker::MatchLink, SelectorChecker::MatchVisited }) {
2412 if (!property.cssValue[index])
2413 continue;
2414 if (index != SelectorChecker::MatchDefault && this->state().style()->insideLink() == InsideLink::NotInside)
2415 continue;
2416
2417 Ref<CSSCustomPropertyValue> valueToApply = CSSCustomPropertyValue::create(downcast<CSSCustomPropertyValue>(*property.cssValue[index]));
2418
2419 if (inCycle) {
2420 state.appliedCustomProperties.add(name); // Make sure we do not try to apply this property again while resolving it.
2421 valueToApply = CSSCustomPropertyValue::createWithID(name, CSSValueInvalid);
2422 }
2423
2424 state.inProgressPropertiesCustom.add(name);
2425
2426 if (WTF::holds_alternative<Ref<CSSVariableReferenceValue>>(valueToApply->value())) {
2427 RefPtr<CSSValue> parsedValue = resolvedVariableValue(CSSPropertyCustom, valueToApply.get(), state);
2428
2429 if (state.appliedCustomProperties.contains(name))
2430 return; // There was a cycle and the value was reset, so bail.
2431
2432 if (!parsedValue)
2433 parsedValue = CSSCustomPropertyValue::createWithID(name, CSSValueUnset);
2434
2435 valueToApply = downcast<CSSCustomPropertyValue>(*parsedValue);
2436 }
2437
2438 if (state.inProgressPropertiesCustom.contains(name)) {
2439 if (index == SelectorChecker::MatchDefault) {
2440 this->state().setApplyPropertyToRegularStyle(true);
2441 this->state().setApplyPropertyToVisitedLinkStyle(false);
2442 }
2443
2444 if (index == SelectorChecker::MatchLink) {
2445 this->state().setApplyPropertyToRegularStyle(true);
2446 this->state().setApplyPropertyToVisitedLinkStyle(false);
2447 }
2448
2449 if (index == SelectorChecker::MatchVisited) {
2450 this->state().setApplyPropertyToRegularStyle(false);
2451 this->state().setApplyPropertyToVisitedLinkStyle(true);
2452 }
2453 applyProperty(CSSPropertyCustom, valueToApply.ptr(), state, index);
2454 }
2455 }
2456
2457 state.inProgressPropertiesCustom.remove(name);
2458 state.appliedCustomProperties.add(name);
2459
2460 for (auto index : { SelectorChecker::MatchDefault, SelectorChecker::MatchLink, SelectorChecker::MatchVisited }) {
2461 if (!property.cssValue[index])
2462 continue;
2463 if (index != SelectorChecker::MatchDefault && this->state().style()->insideLink() == InsideLink::NotInside)
2464 continue;
2465
2466 Ref<CSSCustomPropertyValue> valueToApply = CSSCustomPropertyValue::create(downcast<CSSCustomPropertyValue>(*property.cssValue[index]));
2467
2468 if (inCycle && WTF::holds_alternative<Ref<CSSVariableReferenceValue>>(valueToApply->value())) {
2469 // Resolve this value so that we reset its dependencies.
2470 resolvedVariableValue(CSSPropertyCustom, valueToApply.get(), state);
2471 }
2472 }
2473}
2474
2475void StyleResolver::applyCascadedProperties(int firstProperty, int lastProperty, ApplyCascadedPropertyState& state)
2476{
2477 if (LIKELY(state.cascade->customProperties().isEmpty()))
2478 return applyCascadedPropertiesImpl<CustomPropertyCycleTracking::Disabled>(firstProperty, lastProperty, state);
2479 return applyCascadedPropertiesImpl<CustomPropertyCycleTracking::Enabled>(firstProperty, lastProperty, state);
2480}
2481
2482template<StyleResolver::CustomPropertyCycleTracking TrackCycles>
2483inline void StyleResolver::applyCascadedPropertiesImpl(int firstProperty, int lastProperty, ApplyCascadedPropertyState& state)
2484{
2485 for (int id = firstProperty; id <= lastProperty; ++id) {
2486 CSSPropertyID propertyID = static_cast<CSSPropertyID>(id);
2487 if (!state.cascade->hasProperty(propertyID))
2488 continue;
2489 ASSERT(propertyID != CSSPropertyCustom);
2490 auto& property = state.cascade->property(propertyID);
2491 ASSERT(!shouldApplyPropertyInParseOrder(propertyID));
2492
2493 if (TrackCycles == CustomPropertyCycleTracking::Disabled) {
2494 // If we don't have any custom properties, then there can't be any cycles.
2495 property.apply(*this, state);
2496 } else {
2497 if (UNLIKELY(state.inProgressProperties.get(propertyID))) {
2498 // We are in a cycle (eg. setting font size using registered custom property value containing em).
2499 // So this value should be unset.
2500 state.appliedProperties.set(propertyID);
2501 // This property is in a cycle, and only the root of the call stack will have firstProperty != lastProperty.
2502 ASSERT(firstProperty == lastProperty);
2503 continue;
2504 }
2505
2506 state.inProgressProperties.set(propertyID);
2507 property.apply(*this, state);
2508 state.appliedProperties.set(propertyID);
2509 state.inProgressProperties.set(propertyID, false);
2510 }
2511 }
2512}
2513
2514} // namespace WebCore
2515