diff options
Diffstat (limited to 'Source/WebCore/css/StyleResolver.cpp')
| -rw-r--r-- | Source/WebCore/css/StyleResolver.cpp | 2766 |
1 files changed, 2120 insertions, 646 deletions
diff --git a/Source/WebCore/css/StyleResolver.cpp b/Source/WebCore/css/StyleResolver.cpp index a8f26df84..827388f29 100644 --- a/Source/WebCore/css/StyleResolver.cpp +++ b/Source/WebCore/css/StyleResolver.cpp @@ -1,15 +1,14 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) + * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) - * Copyright (C) 2005-2014 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org> * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (c) 2011, Code Aurora Forum. All rights reserved. * Copyright (C) Research In Motion Limited 2011. All rights reserved. - * Copyright (C) 2012, 2013 Google Inc. All rights reserved. - * Copyright (C) 2014 Igalia S.L. + * Copyright (C) 2012 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -30,10 +29,10 @@ #include "config.h" #include "StyleResolver.h" +#include "Attribute.h" #include "CSSBorderImage.h" #include "CSSCalculationValue.h" #include "CSSCursorImageValue.h" -#include "CSSCustomPropertyValue.h" #include "CSSDefaultStyleSheets.h" #include "CSSFilterImageValue.h" #include "CSSFontFaceRule.h" @@ -41,10 +40,7 @@ #include "CSSFontSelector.h" #include "CSSFontValue.h" #include "CSSFunctionValue.h" -#include "CSSInheritedValue.h" -#include "CSSInitialValue.h" -#include "CSSKeyframeRule.h" -#include "CSSKeyframesRule.h" +#include "CSSGridTemplateValue.h" #include "CSSLineBoxContainValue.h" #include "CSSPageRule.h" #include "CSSParser.h" @@ -58,19 +54,16 @@ #include "CSSSupportsRule.h" #include "CSSTimingFunctionValue.h" #include "CSSValueList.h" -#include "CSSValuePool.h" -#include "CSSVariableDependentValue.h" #include "CachedImage.h" #include "CachedResourceLoader.h" -#include "CachedSVGDocument.h" -#include "CachedSVGDocumentReference.h" #include "CalculationValue.h" #include "ContentData.h" #include "Counter.h" #include "CounterContent.h" #include "CursorList.h" +#include "DeprecatedStyleBuilder.h" +#include "DocumentStyleSheetCollection.h" #include "ElementRuleCollector.h" -#include "FilterOperation.h" #include "Frame.h" #include "FrameSelection.h" #include "FrameView.h" @@ -84,6 +77,7 @@ #include "HTMLStyleElement.h" #include "HTMLTableElement.h" #include "HTMLTextAreaElement.h" +#include "InsertionPoint.h" #include "InspectorInstrumentation.h" #include "KeyframeList.h" #include "LinkHash.h" @@ -92,13 +86,12 @@ #include "MediaList.h" #include "MediaQueryEvaluator.h" #include "NodeRenderStyle.h" +#include "NodeRenderingTraversal.h" #include "Page.h" #include "PageRuleCollector.h" #include "Pair.h" -#include "PseudoElement.h" #include "QuotesData.h" #include "Rect.h" -#include "RenderGrid.h" #include "RenderRegion.h" #include "RenderScrollbar.h" #include "RenderScrollbarTheme.h" @@ -106,17 +99,13 @@ #include "RenderTheme.h" #include "RenderView.h" #include "RuleSet.h" -#include "SVGDocument.h" #include "SVGDocumentExtensions.h" #include "SVGFontFaceElement.h" -#include "SVGNames.h" -#include "SVGSVGElement.h" -#include "SVGURIReference.h" #include "SecurityOrigin.h" +#include "SelectorCheckerFastPath.h" #include "Settings.h" #include "ShadowData.h" #include "ShadowRoot.h" -#include "StyleBuilder.h" #include "StyleCachedImage.h" #include "StyleFontSizeFunctions.h" #include "StyleGeneratedImage.h" @@ -125,7 +114,6 @@ #include "StylePropertyShorthand.h" #include "StyleRule.h" #include "StyleRuleImport.h" -#include "StyleScrollSnapPoints.h" #include "StyleSheetContents.h" #include "StyleSheetList.h" #include "Text.h" @@ -134,20 +122,19 @@ #include "UserAgentStyleSheets.h" #include "ViewportStyleResolver.h" #include "VisitedLinkState.h" -#include "WebKitCSSFilterValue.h" +#include "WebKitCSSKeyframeRule.h" +#include "WebKitCSSKeyframesRule.h" #include "WebKitCSSRegionRule.h" #include "WebKitCSSTransformValue.h" #include "WebKitFontFamilyNames.h" #include "XMLNames.h" #include <bitset> #include <wtf/StdLibExtras.h> -#include <wtf/TemporaryChange.h> #include <wtf/Vector.h> -#include <wtf/text/AtomicStringHash.h> -#if ENABLE(CSS_GRID_LAYOUT) -#include "CSSGridLineNamesValue.h" -#include "CSSGridTemplateAreasValue.h" +#if ENABLE(CSS_FILTERS) +#include "FilterOperation.h" +#include "WebKitCSSFilterValue.h" #endif #if ENABLE(CSS_IMAGE_SET) @@ -159,23 +146,81 @@ #include "DashboardRegion.h" #endif +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLAudioElement.h" +#endif + +#if ENABLE(SVG) +#include "CachedSVGDocument.h" +#include "CachedSVGDocumentReference.h" +#include "SVGDocument.h" +#include "SVGElement.h" +#include "SVGNames.h" +#include "SVGURIReference.h" +#endif + #if ENABLE(VIDEO_TRACK) #include "WebVTTElement.h" #endif -#if ENABLE(CSS_SCROLL_SNAP) -#include "LengthRepeat.h" +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLMediaElement.h" #endif namespace WebCore { using namespace HTMLNames; -static const CSSPropertyID lastHighPriorityProperty = CSSPropertyFontSynthesis; -static const CSSPropertyID firstLowPriorityProperty = static_cast<CSSPropertyID>(lastHighPriorityProperty + 1); +class StyleResolver::CascadedProperties { +public: + CascadedProperties(TextDirection, WritingMode); + + struct Property { + void apply(StyleResolver&); + + CSSPropertyID id; + CSSValue* cssValue[3]; + }; + + bool hasProperty(CSSPropertyID id) const { return m_propertyIsPresent.test(id); } + Property& property(CSSPropertyID); + bool addMatches(const MatchResult&, bool important, int startIndex, int endIndex, bool inheritedOnly = false); + + void set(CSSPropertyID, CSSValue&, unsigned linkMatchType); + void setDeferred(CSSPropertyID, CSSValue&, unsigned linkMatchType); + + void applyDeferredProperties(StyleResolver&); + +private: + bool addStyleProperties(const StyleProperties&, StyleRule&, bool isImportant, bool inheritedOnly, PropertyWhitelistType, unsigned linkMatchType); + static void setPropertyInternal(Property&, CSSPropertyID, CSSValue&, unsigned linkMatchType); + + Property m_properties[numCSSProperties + 1]; + std::bitset<numCSSProperties + 1> m_propertyIsPresent; + + Vector<Property, 8> m_deferredProperties; + + TextDirection m_direction; + WritingMode m_writingMode; +}; static void extractDirectionAndWritingMode(const RenderStyle&, const StyleResolver::MatchResult&, TextDirection&, WritingMode&); +#define HANDLE_INHERIT(prop, Prop) \ +if (isInherit) { \ + m_state.style()->set##Prop(m_state.parentStyle()->prop()); \ + return; \ +} + +#define HANDLE_INHERIT_AND_INITIAL(prop, Prop) \ +HANDLE_INHERIT(prop, Prop) \ +if (isInitial) { \ + m_state.style()->set##Prop(RenderStyle::initial##Prop()); \ + return; \ +} + +RenderStyle* StyleResolver::s_styleNotYetAvailable; + inline void StyleResolver::State::cacheBorderAndBackground() { m_hasUAAppearance = m_style->hasAppearance(); @@ -189,65 +234,36 @@ inline void StyleResolver::State::cacheBorderAndBackground() inline void StyleResolver::State::clear() { m_element = nullptr; + m_styledElement = nullptr; m_parentStyle = nullptr; + m_parentNode = nullptr; m_regionForStyling = nullptr; m_pendingImageProperties.clear(); +#if ENABLE(CSS_FILTERS) && ENABLE(SVG) m_filtersWithPendingSVGDocuments.clear(); - m_cssToLengthConversionData = CSSToLengthConversionData(); +#endif } void StyleResolver::MatchResult::addMatchedProperties(const StyleProperties& properties, StyleRule* rule, unsigned linkMatchType, PropertyWhitelistType propertyWhitelistType) { - m_matchedProperties.grow(m_matchedProperties.size() + 1); - StyleResolver::MatchedProperties& newProperties = m_matchedProperties.last(); + matchedProperties.grow(matchedProperties.size() + 1); + StyleResolver::MatchedProperties& newProperties = matchedProperties.last(); newProperties.properties = const_cast<StyleProperties*>(&properties); newProperties.linkMatchType = linkMatchType; newProperties.whitelistType = propertyWhitelistType; matchedRules.append(rule); - - if (isCacheable) { - for (unsigned i = 0, count = properties.propertyCount(); i < count; ++i) { - // Currently the property cache only copy the non-inherited values and resolve - // the inherited ones. - // Here we define some exception were we have to resolve some properties that are not inherited - // by default. If those exceptions become too common on the web, it should be possible - // to build a list of exception to resolve instead of completely disabling the cache. - - StyleProperties::PropertyReference current = properties.propertyAt(i); - if (!current.isInherited()) { - // If the property value is explicitly inherited, we need to apply further non-inherited properties - // as they might override the value inherited here. For this reason we don't allow declarations with - // explicitly inherited properties to be cached. - const CSSValue& value = *current.value(); - if (value.isInheritedValue()) { - isCacheable = false; - break; - } - - // The value currentColor has implicitely the same side effect. It depends on the value of color, - // which is an inherited value, making the non-inherited property implicitly inherited. - if (is<CSSPrimitiveValue>(value) && downcast<CSSPrimitiveValue>(value).getValueID() == CSSValueCurrentcolor) { - isCacheable = false; - break; - } - - if (value.isVariableDependentValue()) { - isCacheable = false; - break; - } - } - } - } } -StyleResolver::StyleResolver(Document& document) +StyleResolver::StyleResolver(Document& document, bool matchAuthorAndUserStyles) : m_matchedPropertiesCacheAdditionsSinceLastSweep(0) - , m_matchedPropertiesCacheSweepTimer(*this, &StyleResolver::sweepMatchedPropertiesCache) + , m_matchedPropertiesCacheSweepTimer(this, &StyleResolver::sweepMatchedPropertiesCache) , m_document(document) - , m_matchAuthorAndUserStyles(m_document.settings() ? m_document.settings()->authorAndUserStylesEnabled() : true) + , m_matchAuthorAndUserStyles(matchAuthorAndUserStyles) + , m_fontSelector(CSSFontSelector::create(&m_document)) #if ENABLE(CSS_DEVICE_ADAPTATION) , m_viewportStyleResolver(ViewportStyleResolver::create(&document)) #endif + , m_deprecatedStyleBuilder(DeprecatedStyleBuilder::sharedStyleBuilder()) , m_styleMap(this) { Element* root = m_document.documentElement(); @@ -267,35 +283,81 @@ StyleResolver::StyleResolver(Document& document) m_medium = std::make_unique<MediaQueryEvaluator>("all"); if (root) - m_rootDefaultStyle = styleForElement(*root, m_document.renderStyle(), MatchOnlyUserAgentRules); + m_rootDefaultStyle = styleForElement(root, 0, DisallowStyleSharing, MatchOnlyUserAgentRules); if (m_rootDefaultStyle && view) m_medium = std::make_unique<MediaQueryEvaluator>(view->mediaType(), &view->frame(), m_rootDefaultStyle.get()); m_ruleSets.resetAuthorStyle(); - m_ruleSets.initUserStyle(m_document.extensionStyleSheets(), *m_medium, *this); + DocumentStyleSheetCollection& styleSheetCollection = m_document.styleSheetCollection(); + m_ruleSets.initUserStyle(styleSheetCollection, *m_medium, *this); #if ENABLE(SVG_FONTS) if (m_document.svgExtensions()) { const HashSet<SVGFontFaceElement*>& svgFontFaceElements = m_document.svgExtensions()->svgFontFaceElements(); - for (auto* svgFontFaceElement : svgFontFaceElements) - m_document.fontSelector().addFontFaceRule(svgFontFaceElement->fontFaceRule(), svgFontFaceElement->isInUserAgentShadowTree()); + HashSet<SVGFontFaceElement*>::const_iterator end = svgFontFaceElements.end(); + for (HashSet<SVGFontFaceElement*>::const_iterator it = svgFontFaceElements.begin(); it != end; ++it) + fontSelector()->addFontFaceRule((*it)->fontFaceRule()); } #endif + + appendAuthorStyleSheets(0, styleSheetCollection.activeAuthorStyleSheets()); } -void StyleResolver::appendAuthorStyleSheets(const Vector<RefPtr<CSSStyleSheet>>& styleSheets) +void StyleResolver::appendAuthorStyleSheets(unsigned firstNew, const Vector<RefPtr<CSSStyleSheet>>& styleSheets) { - m_ruleSets.appendAuthorStyleSheets(styleSheets, m_medium.get(), m_inspectorCSSOMWrappers, this); + m_ruleSets.appendAuthorStyleSheets(firstNew, styleSheets, m_medium.get(), m_inspectorCSSOMWrappers, document().isViewSource(), this); if (auto renderView = document().renderView()) - renderView->style().fontCascade().update(&document().fontSelector()); + renderView->style().font().update(fontSelector()); #if ENABLE(CSS_DEVICE_ADAPTATION) viewportStyleResolver()->resolve(); #endif } +void StyleResolver::pushParentElement(Element* parent) +{ + const ContainerNode* parentsParent = parent->parentOrShadowHostElement(); + + // We are not always invoked consistently. For example, script execution can cause us to enter + // style recalc in the middle of tree building. We may also be invoked from somewhere within the tree. + // Reset the stack in this case, or if we see a new root element. + // Otherwise just push the new parent. + if (!parentsParent || m_selectorFilter.parentStackIsEmpty()) + m_selectorFilter.setupParentStack(parent); + else + m_selectorFilter.pushParent(parent); + + // Note: We mustn't skip ShadowRoot nodes for the scope stack. + if (m_scopeResolver) + m_scopeResolver->push(parent, parent->parentOrShadowHostNode()); +} + +void StyleResolver::popParentElement(Element* parent) +{ + // Note that we may get invoked for some random elements in some wacky cases during style resolve. + // Pause maintaining the stack in this case. + if (m_selectorFilter.parentStackIsConsistent(parent)) + m_selectorFilter.popParent(); + if (m_scopeResolver) + m_scopeResolver->pop(parent); +} + +void StyleResolver::pushParentShadowRoot(const ShadowRoot* shadowRoot) +{ + ASSERT(shadowRoot->hostElement()); + if (m_scopeResolver) + m_scopeResolver->push(shadowRoot, shadowRoot->hostElement()); +} + +void StyleResolver::popParentShadowRoot(const ShadowRoot* shadowRoot) +{ + ASSERT(shadowRoot->hostElement()); + if (m_scopeResolver) + m_scopeResolver->pop(shadowRoot); +} + // This is a simplified style setting function for keyframe styles void StyleResolver::addKeyframeStyle(PassRefPtr<StyleRuleKeyframes> rule) { @@ -305,14 +367,14 @@ void StyleResolver::addKeyframeStyle(PassRefPtr<StyleRuleKeyframes> rule) StyleResolver::~StyleResolver() { - RELEASE_ASSERT(!m_inLoadPendingImages); + m_fontSelector->clearDocument(); #if ENABLE(CSS_DEVICE_ADAPTATION) m_viewportStyleResolver->clearDocument(); #endif } -void StyleResolver::sweepMatchedPropertiesCache() +void StyleResolver::sweepMatchedPropertiesCache(Timer<StyleResolver>*) { // Look for cache entries containing a style declaration with a single ref and remove them. // This may happen when an element attribute mutation causes it to generate a new inlineStyle() @@ -335,34 +397,396 @@ void StyleResolver::sweepMatchedPropertiesCache() m_matchedPropertiesCacheAdditionsSinceLastSweep = 0; } -StyleResolver::State::State(Element& element, RenderStyle* parentStyle, const RenderRegion* regionForStyling, const SelectorFilter* selectorFilter) - : m_element(&element) - , m_parentStyle(parentStyle) - , m_regionForStyling(regionForStyling) - , m_elementLinkState(element.document().visitedLinkState().determineLinkState(element)) - , m_selectorFilter(selectorFilter) +inline bool StyleResolver::styleSharingCandidateMatchesHostRules() { - bool resetStyleInheritance = hasShadowRootParent(element) && downcast<ShadowRoot>(element.parentNode())->resetStyleInheritance(); - if (resetStyleInheritance) - m_parentStyle = nullptr; +#if ENABLE(SHADOW_DOM) + return m_scopeResolver && m_scopeResolver->styleSharingCandidateMatchesHostRules(m_state.element()); +#else + return false; +#endif +} - auto& document = element.document(); - auto* documentElement = document.documentElement(); - m_rootElementStyle = (!documentElement || documentElement == &element) ? document.renderStyle() : documentElement->renderStyle(); +bool StyleResolver::classNamesAffectedByRules(const SpaceSplitString& classNames) const +{ + for (unsigned i = 0; i < classNames.size(); ++i) { + if (m_ruleSets.features().classesInRules.contains(classNames[i].impl())) + return true; + } + return false; +} - updateConversionData(); +inline void StyleResolver::State::initElement(Element* e) +{ + m_element = e; + m_styledElement = e && e->isStyledElement() ? toStyledElement(e) : nullptr; + m_elementLinkState = e ? e->document().visitedLinkState().determineLinkState(e) : NotInsideLink; } -inline void StyleResolver::State::updateConversionData() +inline void StyleResolver::initElement(Element* e) { - m_cssToLengthConversionData = CSSToLengthConversionData(m_style.get(), m_rootElementStyle, m_element ? document().renderView() : nullptr); + if (m_state.element() != e) { + m_state.initElement(e); + if (e && e == e->document().documentElement()) { + e->document().setDirectionSetOnDocumentElement(false); + e->document().setWritingModeSetOnDocumentElement(false); + } + } +} + +inline void StyleResolver::State::initForStyleResolve(Document& document, Element* e, RenderStyle* parentStyle, RenderRegion* regionForStyling) +{ + m_regionForStyling = regionForStyling; + + if (e) { + m_parentNode = NodeRenderingTraversal::parent(e); + bool resetStyleInheritance = hasShadowRootParent(*e) && toShadowRoot(e->parentNode())->resetStyleInheritance(); + m_parentStyle = resetStyleInheritance ? 0 : + parentStyle ? parentStyle : + m_parentNode ? m_parentNode->renderStyle() : 0; + } else { + m_parentNode = 0; + m_parentStyle = parentStyle; + } + + Node* docElement = e ? e->document().documentElement() : 0; + RenderStyle* docStyle = document.renderStyle(); + m_rootElementStyle = docElement && e != docElement ? docElement->renderStyle() : docStyle; + + m_style = 0; + m_pendingImageProperties.clear(); + m_fontDirty = false; } -inline void StyleResolver::State::setStyle(Ref<RenderStyle>&& style) +static const unsigned cStyleSearchThreshold = 10; +static const unsigned cStyleSearchLevelThreshold = 10; + +static inline bool parentElementPreventsSharing(const Element* parentElement) { - m_style = WTFMove(style); - updateConversionData(); + if (!parentElement) + return false; + return parentElement->hasFlagsSetDuringStylingOfChildren(); } + +Node* StyleResolver::locateCousinList(Element* parent, unsigned& visitedNodeCount) const +{ + if (visitedNodeCount >= cStyleSearchThreshold * cStyleSearchLevelThreshold) + return 0; + if (!parent || !parent->isStyledElement()) + return 0; + StyledElement* p = toStyledElement(parent); + if (p->inlineStyle()) + return 0; +#if ENABLE(SVG) + if (p->isSVGElement() && toSVGElement(p)->animatedSMILStyleProperties()) + return 0; +#endif + if (p->hasID() && m_ruleSets.features().idsInRules.contains(p->idForStyleResolution().impl())) + return 0; + + RenderStyle* parentStyle = p->renderStyle(); + unsigned subcount = 0; + Node* thisCousin = p; + Node* currentNode = p->previousSibling(); + + // Reserve the tries for this level. This effectively makes sure that the algorithm + // will never go deeper than cStyleSearchLevelThreshold levels into recursion. + visitedNodeCount += cStyleSearchThreshold; + while (thisCousin) { + while (currentNode) { + ++subcount; + if (currentNode->renderStyle() == parentStyle && currentNode->lastChild() + && currentNode->isElementNode() && !parentElementPreventsSharing(toElement(currentNode)) +#if ENABLE(SHADOW_DOM) + && !toElement(currentNode)->authorShadowRoot() +#endif + ) { + // Adjust for unused reserved tries. + visitedNodeCount -= cStyleSearchThreshold - subcount; + return currentNode->lastChild(); + } + if (subcount >= cStyleSearchThreshold) + return 0; + currentNode = currentNode->previousSibling(); + } + currentNode = locateCousinList(thisCousin->parentElement(), visitedNodeCount); + thisCousin = currentNode; + } + + return 0; +} + +bool StyleResolver::styleSharingCandidateMatchesRuleSet(RuleSet* ruleSet) +{ + if (!ruleSet) + return false; + + ElementRuleCollector collector(this, m_state); + return collector.hasAnyMatchingRules(ruleSet); +} + +bool StyleResolver::canShareStyleWithControl(StyledElement* element) const +{ + const State& state = m_state; + HTMLInputElement* thisInputElement = element->toInputElement(); + HTMLInputElement* otherInputElement = state.element()->toInputElement(); + + if (!thisInputElement || !otherInputElement) + return false; + + if (thisInputElement->elementData() != otherInputElement->elementData()) { + if (thisInputElement->fastGetAttribute(typeAttr) != otherInputElement->fastGetAttribute(typeAttr)) + return false; + if (thisInputElement->fastGetAttribute(readonlyAttr) != otherInputElement->fastGetAttribute(readonlyAttr)) + return false; + } + + if (thisInputElement->isAutofilled() != otherInputElement->isAutofilled()) + return false; + if (thisInputElement->shouldAppearChecked() != otherInputElement->shouldAppearChecked()) + return false; + if (thisInputElement->shouldAppearIndeterminate() != otherInputElement->shouldAppearIndeterminate()) + return false; + if (thisInputElement->isRequired() != otherInputElement->isRequired()) + return false; + + if (element->isDisabledFormControl() != state.element()->isDisabledFormControl()) + return false; + + if (element->isDefaultButtonForForm() != state.element()->isDefaultButtonForForm()) + return false; + + if (state.document().containsValidityStyleRules()) { + bool willValidate = element->willValidate(); + + if (willValidate != state.element()->willValidate()) + return false; + + if (willValidate && (element->isValidFormControlElement() != state.element()->isValidFormControlElement())) + return false; + + if (element->isInRange() != state.element()->isInRange()) + return false; + + if (element->isOutOfRange() != state.element()->isOutOfRange()) + return false; + } + + return true; +} + +static inline bool elementHasDirectionAuto(Element* element) +{ + // FIXME: This line is surprisingly hot, we may wish to inline hasDirectionAuto into StyleResolver. + return element->isHTMLElement() && toHTMLElement(element)->hasDirectionAuto(); +} + +bool StyleResolver::sharingCandidateHasIdenticalStyleAffectingAttributes(StyledElement* sharingCandidate) const +{ + const State& state = m_state; + if (state.element()->elementData() == sharingCandidate->elementData()) + return true; + if (state.element()->fastGetAttribute(XMLNames::langAttr) != sharingCandidate->fastGetAttribute(XMLNames::langAttr)) + return false; + if (state.element()->fastGetAttribute(langAttr) != sharingCandidate->fastGetAttribute(langAttr)) + return false; + + if (!state.elementAffectedByClassRules()) { + if (sharingCandidate->hasClass() && classNamesAffectedByRules(sharingCandidate->classNames())) + return false; + } else if (sharingCandidate->hasClass()) { +#if ENABLE(SVG) + // SVG elements require a (slow!) getAttribute comparision because "class" is an animatable attribute for SVG. + if (state.element()->isSVGElement()) { + if (state.element()->getAttribute(classAttr) != sharingCandidate->getAttribute(classAttr)) + return false; + } else { +#endif + if (state.element()->classNames() != sharingCandidate->classNames()) + return false; +#if ENABLE(SVG) + } +#endif + } else + return false; + + if (state.styledElement()->presentationAttributeStyle() != sharingCandidate->presentationAttributeStyle()) + return false; + +#if ENABLE(PROGRESS_ELEMENT) + if (state.element()->hasTagName(progressTag)) { + if (state.element()->shouldAppearIndeterminate() != sharingCandidate->shouldAppearIndeterminate()) + return false; + } +#endif + + return true; +} + +bool StyleResolver::canShareStyleWithElement(StyledElement* element) const +{ + RenderStyle* style = element->renderStyle(); + const State& state = m_state; + + if (!style) + return false; + if (style->unique()) + return false; + if (style->hasUniquePseudoStyle()) + return false; + if (element->tagQName() != state.element()->tagQName()) + return false; + if (element->inlineStyle()) + return false; + if (element->needsStyleRecalc()) + return false; +#if ENABLE(SVG) + if (element->isSVGElement() && toSVGElement(element)->animatedSMILStyleProperties()) + return false; +#endif + if (element->isLink() != state.element()->isLink()) + return false; + if (element->hovered() != state.element()->hovered()) + return false; + if (element->active() != state.element()->active()) + return false; + if (element->focused() != state.element()->focused()) + return false; + if (element->shadowPseudoId() != state.element()->shadowPseudoId()) + return false; + if (element == element->document().cssTarget()) + return false; + if (!sharingCandidateHasIdenticalStyleAffectingAttributes(element)) + return false; + if (element->additionalPresentationAttributeStyle() != state.styledElement()->additionalPresentationAttributeStyle()) + return false; + + if (element->hasID() && m_ruleSets.features().idsInRules.contains(element->idForStyleResolution().impl())) + return false; + + // FIXME: We should share style for option and optgroup whenever possible. + // Before doing so, we need to resolve issues in HTMLSelectElement::recalcListItems + // and RenderMenuList::setText. See also https://bugs.webkit.org/show_bug.cgi?id=88405 + if (isHTMLOptionElement(element) || isHTMLOptGroupElement(element)) + return false; + + bool isControl = element->isFormControlElement(); + + if (isControl != state.element()->isFormControlElement()) + return false; + + if (isControl && !canShareStyleWithControl(element)) + return false; + + if (style->transitions() || style->animations()) + return false; + +#if USE(ACCELERATED_COMPOSITING) + // Turn off style sharing for elements that can gain layers for reasons outside of the style system. + // See comments in RenderObject::setStyle(). + if (element->hasTagName(iframeTag) || element->hasTagName(frameTag) || element->hasTagName(embedTag) || element->hasTagName(objectTag) || element->hasTagName(appletTag) || element->hasTagName(canvasTag)) + return false; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + // With proxying, the media elements are backed by a RenderEmbeddedObject. + if ((element->hasTagName(videoTag) || element->hasTagName(audioTag)) && toHTMLMediaElement(element)->shouldUseVideoPluginProxy()) + return false; +#endif + +#endif + + if (elementHasDirectionAuto(element)) + return false; + + if (element->isLink() && state.elementLinkState() != style->insideLink()) + return false; + +#if ENABLE(VIDEO_TRACK) + // Deny sharing styles between WebVTT and non-WebVTT nodes. + if (element->isWebVTTElement() != state.element()->isWebVTTElement()) + return false; + + if (element->isWebVTTElement() && state.element()->isWebVTTElement() && toWebVTTElement(element)->isPastNode() != toWebVTTElement(state.element())->isPastNode()) + return false; +#endif + +#if ENABLE(FULLSCREEN_API) + if (element == element->document().webkitCurrentFullScreenElement() || state.element() == state.document().webkitCurrentFullScreenElement()) + return false; +#endif + return true; +} + +inline StyledElement* StyleResolver::findSiblingForStyleSharing(Node* node, unsigned& count) const +{ + for (; node; node = node->previousSibling()) { + if (!node->isStyledElement()) + continue; + if (canShareStyleWithElement(toStyledElement(node))) + break; + if (count++ == cStyleSearchThreshold) + return 0; + } + return toStyledElement(node); +} + +RenderStyle* StyleResolver::locateSharedStyle() +{ + State& state = m_state; + if (!state.styledElement() || !state.parentStyle()) + return 0; + + // If the element has inline style it is probably unique. + if (state.styledElement()->inlineStyle()) + return 0; +#if ENABLE(SVG) + if (state.styledElement()->isSVGElement() && toSVGElement(state.styledElement())->animatedSMILStyleProperties()) + return 0; +#endif + // Ids stop style sharing if they show up in the stylesheets. + if (state.styledElement()->hasID() && m_ruleSets.features().idsInRules.contains(state.styledElement()->idForStyleResolution().impl())) + return 0; + if (parentElementPreventsSharing(state.element()->parentElement())) + return 0; + if (state.element() == state.document().cssTarget()) + return 0; + if (elementHasDirectionAuto(state.element())) + return 0; + + // Cache whether state.element is affected by any known class selectors. + // FIXME: This shouldn't be a member variable. The style sharing code could be factored out of StyleResolver. + state.setElementAffectedByClassRules(state.element() && state.element()->hasClass() && classNamesAffectedByRules(state.element()->classNames())); + + // Check previous siblings and their cousins. + unsigned count = 0; + unsigned visitedNodeCount = 0; + StyledElement* shareElement = 0; + Node* cousinList = state.styledElement()->previousSibling(); + while (cousinList) { + shareElement = findSiblingForStyleSharing(cousinList, count); + if (shareElement) + break; + cousinList = locateCousinList(cousinList->parentElement(), visitedNodeCount); + } + + // If we have exhausted all our budget or our cousins. + if (!shareElement) + return 0; + + // Can't share if sibling rules apply. This is checked at the end as it should rarely fail. + if (styleSharingCandidateMatchesRuleSet(m_ruleSets.sibling())) + return 0; + // Can't share if attribute rules apply. + if (styleSharingCandidateMatchesRuleSet(m_ruleSets.uncommonAttribute())) + return 0; + // Can't share if @host @-rules apply. + if (styleSharingCandidateMatchesHostRules()) + return 0; + // Tracking child index requires unique style for each node. This may get set by the sibling rule match above. + if (parentElementPreventsSharing(state.element()->parentElement())) + return 0; + return shareElement->renderStyle(); +} + static inline bool isAtShadowBoundary(const Element* element) { if (!element) @@ -371,35 +795,56 @@ static inline bool isAtShadowBoundary(const Element* element) return parentNode && parentNode->isShadowRoot(); } -Ref<RenderStyle> StyleResolver::styleForElement(Element& element, RenderStyle* parentStyle, RuleMatchingBehavior matchingBehavior, const RenderRegion* regionForStyling, const SelectorFilter* selectorFilter) +PassRef<RenderStyle> StyleResolver::styleForElement(Element* element, RenderStyle* defaultParent, + StyleSharingBehavior sharingBehavior, RuleMatchingBehavior matchingBehavior, RenderRegion* regionForStyling) { - RELEASE_ASSERT(!m_inLoadPendingImages); + // Once an element has a renderer, we don't try to destroy it, since otherwise the renderer + // will vanish if a style recalc happens during loading. + if (sharingBehavior == AllowStyleSharing && !element->document().haveStylesheetsLoaded() && !element->renderer()) { + if (!s_styleNotYetAvailable) { + s_styleNotYetAvailable = &RenderStyle::create().leakRef(); + s_styleNotYetAvailable->setDisplay(NONE); + s_styleNotYetAvailable->font().update(m_fontSelector); + } + element->document().setHasNodesWithPlaceholderStyle(); + return *s_styleNotYetAvailable; + } - m_state = State(element, parentStyle, regionForStyling, selectorFilter); State& state = m_state; + initElement(element); + state.initForStyleResolve(document(), element, defaultParent, regionForStyling); + if (sharingBehavior == AllowStyleSharing) { + if (RenderStyle* sharedStyle = locateSharedStyle()) { + state.clear(); + return *sharedStyle; + } + } if (state.parentStyle()) { state.setStyle(RenderStyle::create()); - state.style()->inheritFrom(state.parentStyle(), isAtShadowBoundary(&element) ? RenderStyle::AtShadowBoundary : RenderStyle::NotAtShadowBoundary); + state.style()->inheritFrom(state.parentStyle(), isAtShadowBoundary(element) ? RenderStyle::AtShadowBoundary : RenderStyle::NotAtShadowBoundary); } else { state.setStyle(defaultStyleForElement()); state.setParentStyle(RenderStyle::clone(state.style())); } - if (element.isLink()) { + if (element->isLink()) { state.style()->setIsLink(true); EInsideLink linkState = state.elementLinkState(); if (linkState != NotInsideLink) { - bool forceVisited = InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassVisited); + bool forceVisited = InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoVisited); if (forceVisited) linkState = InsideVisitedLink; } state.style()->setInsideLink(linkState); } - CSSDefaultStyleSheets::ensureDefaultStyleSheetsForElement(element); + bool needsCollection = false; + CSSDefaultStyleSheets::ensureDefaultStyleSheetsForElement(element, needsCollection); + if (needsCollection) + m_ruleSets.collectFeatures(document().isViewSource(), m_scopeResolver.get()); - ElementRuleCollector collector(element, state.style(), m_ruleSets, m_state.selectorFilter()); + ElementRuleCollector collector(this, state); collector.setRegionForStyling(regionForStyling); collector.setMedium(m_medium.get()); @@ -411,21 +856,18 @@ Ref<RenderStyle> StyleResolver::styleForElement(Element& element, RenderStyle* p applyMatchedProperties(collector.matchedResult(), element); // Clean up our style object's display and text decorations (among other fixups). - adjustRenderStyle(*state.style(), *state.parentStyle(), &element); - - if (state.style()->hasViewportUnits()) - document().setHasStyleWithViewportUnits(); + adjustRenderStyle(*state.style(), *state.parentStyle(), element); state.clear(); // Clear out for the next resolve. + document().didAccessStyleResolver(); + // Now return the style. return state.takeStyle(); } -Ref<RenderStyle> StyleResolver::styleForKeyframe(const RenderStyle* elementStyle, const StyleKeyframe* keyframe, KeyframeValue& keyframeValue) +PassRef<RenderStyle> StyleResolver::styleForKeyframe(const RenderStyle* elementStyle, const StyleKeyframe* keyframe, KeyframeValue& keyframeValue) { - RELEASE_ASSERT(!m_inLoadPendingImages); - MatchResult result; result.addMatchedProperties(keyframe->properties()); @@ -435,7 +877,7 @@ Ref<RenderStyle> StyleResolver::styleForKeyframe(const RenderStyle* elementStyle // Create the style state.setStyle(RenderStyle::clone(elementStyle)); - state.setParentStyle(RenderStyle::clone(elementStyle)); + state.setLineHeightValue(0); TextDirection direction; WritingMode writingMode; @@ -444,25 +886,25 @@ Ref<RenderStyle> StyleResolver::styleForKeyframe(const RenderStyle* elementStyle // We don't need to bother with !important. Since there is only ever one // decl, there's nothing to override. So just add the first properties. CascadedProperties cascade(direction, writingMode); - cascade.addMatches(result, false, 0, result.matchedProperties().size() - 1); - - // Resolve custom properties first. - applyCascadedProperties(cascade, CSSPropertyCustom, CSSPropertyCustom, &result); + cascade.addMatches(result, false, 0, result.matchedProperties.size() - 1); - applyCascadedProperties(cascade, firstCSSProperty, lastHighPriorityProperty, &result); + applyCascadedProperties(cascade, firstCSSProperty, CSSPropertyLineHeight); - // If our font got dirtied, update it now. + // If our font got dirtied, go ahead and update it now. updateFont(); + // Line-height is set when we are sure we decided on the font-size + if (state.lineHeightValue()) + applyProperty(CSSPropertyLineHeight, state.lineHeightValue()); + // Now do rest of the properties. - applyCascadedProperties(cascade, firstLowPriorityProperty, lastCSSProperty, &result); + applyCascadedProperties(cascade, CSSPropertyBackground, lastCSSProperty); - // If our font got dirtied by one of the non-essential font props, update it a second time. + // If our font got dirtied by one of the non-essential font props, + // go ahead and update it a second time. updateFont(); - cascade.applyDeferredProperties(*this, &result); - - adjustRenderStyle(*state.style(), *state.parentStyle(), nullptr); + cascade.applyDeferredProperties(*this); // Start loading resources referenced by this style. loadPendingResources(); @@ -473,19 +915,21 @@ Ref<RenderStyle> StyleResolver::styleForKeyframe(const RenderStyle* elementStyle CSSPropertyID property = keyframe->properties().propertyAt(i).id(); // Timing-function within keyframes is special, because it is not animated; it just // describes the timing function between this keyframe and the next. - if (property != CSSPropertyWebkitAnimationTimingFunction && property != CSSPropertyAnimationTimingFunction) + if (property != CSSPropertyWebkitAnimationTimingFunction) keyframeValue.addProperty(property); } + document().didAccessStyleResolver(); + return state.takeStyle(); } -void StyleResolver::keyframeStylesForAnimation(Element& element, const RenderStyle* elementStyle, KeyframeList& list) +void StyleResolver::keyframeStylesForAnimation(Element* e, const RenderStyle* elementStyle, KeyframeList& list) { list.clear(); // Get the keyframesRule for this name - if (list.animationName().isEmpty()) + if (!e || list.animationName().isEmpty()) return; m_keyframesRuleMap.checkConsistency(); @@ -500,16 +944,19 @@ void StyleResolver::keyframeStylesForAnimation(Element& element, const RenderSty const Vector<RefPtr<StyleKeyframe>>& keyframes = keyframesRule->keyframes(); for (unsigned i = 0; i < keyframes.size(); ++i) { // Apply the declaration to the style. This is a simplified version of the logic in styleForElement - m_state = State(element, nullptr); + initElement(e); + m_state.initForStyleResolve(document(), e); const StyleKeyframe* keyframe = keyframes[i].get(); - KeyframeValue keyframeValue(0, nullptr); + KeyframeValue keyframeValue(0, 0); keyframeValue.setStyle(styleForKeyframe(elementStyle, keyframe, keyframeValue)); // Add this keyframe style to all the indicated key times - for (auto& key: keyframe->keys()) { - keyframeValue.setKey(key); + Vector<double> keys; + keyframe->getKeys(keys); + for (size_t keyIndex = 0; keyIndex < keys.size(); ++keyIndex) { + keyframeValue.setKey(keys[keyIndex]); list.insert(keyframeValue); } } @@ -519,10 +966,10 @@ void StyleResolver::keyframeStylesForAnimation(Element& element, const RenderSty if (initialListSize > 0 && list[0].key()) { static StyleKeyframe* zeroPercentKeyframe; if (!zeroPercentKeyframe) { - zeroPercentKeyframe = &StyleKeyframe::create(MutableStyleProperties::create()).leakRef(); + zeroPercentKeyframe = StyleKeyframe::create(MutableStyleProperties::create()).leakRef(); zeroPercentKeyframe->setKeyText("0%"); } - KeyframeValue keyframeValue(0, nullptr); + KeyframeValue keyframeValue(0, 0); keyframeValue.setStyle(styleForKeyframe(elementStyle, zeroPercentKeyframe, keyframeValue)); list.insert(keyframeValue); } @@ -531,21 +978,27 @@ void StyleResolver::keyframeStylesForAnimation(Element& element, const RenderSty if (initialListSize > 0 && (list[list.size() - 1].key() != 1)) { static StyleKeyframe* hundredPercentKeyframe; if (!hundredPercentKeyframe) { - hundredPercentKeyframe = &StyleKeyframe::create(MutableStyleProperties::create()).leakRef(); + hundredPercentKeyframe = StyleKeyframe::create(MutableStyleProperties::create()).leakRef(); hundredPercentKeyframe->setKeyText("100%"); } - KeyframeValue keyframeValue(1, nullptr); + KeyframeValue keyframeValue(1, 0); keyframeValue.setStyle(styleForKeyframe(elementStyle, hundredPercentKeyframe, keyframeValue)); list.insert(keyframeValue); } } -PassRefPtr<RenderStyle> StyleResolver::pseudoStyleForElement(Element& element, const PseudoStyleRequest& pseudoStyleRequest, RenderStyle& parentStyle) +PassRefPtr<RenderStyle> StyleResolver::pseudoStyleForElement(Element* e, const PseudoStyleRequest& pseudoStyleRequest, RenderStyle* parentStyle) { - m_state = State(element, &parentStyle); + ASSERT(parentStyle); + if (!e) + return 0; State& state = m_state; + initElement(e); + + state.initForStyleResolve(document(), e, parentStyle); + if (m_state.parentStyle()) { state.setStyle(RenderStyle::create()); state.style()->inheritFrom(m_state.parentStyle()); @@ -558,7 +1011,7 @@ PassRefPtr<RenderStyle> StyleResolver::pseudoStyleForElement(Element& element, c // those rules. // Check UA, user and author rules. - ElementRuleCollector collector(element, m_state.style(), m_ruleSets, m_state.selectorFilter()); + ElementRuleCollector collector(this, state); collector.setPseudoStyleRequest(pseudoStyleRequest); collector.setMedium(m_medium.get()); collector.matchUARules(); @@ -568,41 +1021,35 @@ PassRefPtr<RenderStyle> StyleResolver::pseudoStyleForElement(Element& element, c collector.matchAuthorRules(false); } - if (collector.matchedResult().matchedProperties().isEmpty()) - return nullptr; + if (collector.matchedResult().matchedProperties.isEmpty()) + return 0; state.style()->setStyleType(pseudoStyleRequest.pseudoId); - applyMatchedProperties(collector.matchedResult(), element); + applyMatchedProperties(collector.matchedResult(), e); // Clean up our style object's display and text decorations (among other fixups). - adjustRenderStyle(*state.style(), *m_state.parentStyle(), nullptr); - - if (state.style()->hasViewportUnits()) - document().setHasStyleWithViewportUnits(); + adjustRenderStyle(*state.style(), *m_state.parentStyle(), 0); // Start loading resources referenced by this style. loadPendingResources(); + document().didAccessStyleResolver(); + // Now return the style. return state.takeStyle(); } -Ref<RenderStyle> StyleResolver::styleForPage(int pageIndex) +PassRef<RenderStyle> StyleResolver::styleForPage(int pageIndex) { - RELEASE_ASSERT(!m_inLoadPendingImages); - - auto* documentElement = m_document.documentElement(); - if (!documentElement) - return RenderStyle::create(); - - m_state = State(*documentElement, m_document.renderStyle()); + m_state.initForStyleResolve(document(), document().documentElement()); // m_rootElementStyle will be set to the document style. m_state.setStyle(RenderStyle::create()); m_state.style()->inheritFrom(m_state.rootElementStyle()); PageRuleCollector collector(m_state, m_ruleSets); collector.matchAllPageRules(pageIndex); + m_state.setLineHeightValue(0); MatchResult& result = collector.matchedResult(); @@ -611,36 +1058,39 @@ Ref<RenderStyle> StyleResolver::styleForPage(int pageIndex) extractDirectionAndWritingMode(*m_state.style(), result, direction, writingMode); CascadedProperties cascade(direction, writingMode); - cascade.addMatches(result, false, 0, result.matchedProperties().size() - 1); - - // Resolve custom properties first. - applyCascadedProperties(cascade, CSSPropertyCustom, CSSPropertyCustom, &result); + cascade.addMatches(result, false, 0, result.matchedProperties.size() - 1); - applyCascadedProperties(cascade, firstCSSProperty, lastHighPriorityProperty, &result); + applyCascadedProperties(cascade, firstCSSProperty, CSSPropertyLineHeight); - // If our font got dirtied, update it now. + // If our font got dirtied, go ahead and update it now. updateFont(); - applyCascadedProperties(cascade, firstLowPriorityProperty, lastCSSProperty, &result); + // Line-height is set when we are sure we decided on the font-size. + if (m_state.lineHeightValue()) + applyProperty(CSSPropertyLineHeight, m_state.lineHeightValue()); + + applyCascadedProperties(cascade, CSSPropertyBackground, lastCSSProperty); - cascade.applyDeferredProperties(*this, &result); + cascade.applyDeferredProperties(*this); // Start loading resources referenced by this style. loadPendingResources(); + document().didAccessStyleResolver(); + // Now return the style. return m_state.takeStyle(); } -Ref<RenderStyle> StyleResolver::defaultStyleForElement() +PassRef<RenderStyle> StyleResolver::defaultStyleForElement() { m_state.setStyle(RenderStyle::create()); // Make sure our fonts are initialized if we don't inherit them from our parent style. - initializeFontStyle(documentSettings()); - if (documentSettings()) - m_state.style()->fontCascade().update(&document().fontSelector()); - else - m_state.style()->fontCascade().update(nullptr); + if (Settings* settings = documentSettings()) { + initializeFontStyle(settings); + m_state.style()->font().update(fontSelector()); + } else + m_state.style()->font().update(0); return m_state.takeStyle(); } @@ -651,18 +1101,18 @@ static void addIntrinsicMargins(RenderStyle& style) const int intrinsicMargin = 2 * style.effectiveZoom(); // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed. - // FIXME: Using "hasQuirk" to decide the margin wasn't set is kind of lame. + // FIXME: Using "quirk" to decide the margin wasn't set is kind of lame. if (style.width().isIntrinsicOrAuto()) { - if (style.marginLeft().hasQuirk()) + if (style.marginLeft().quirk()) style.setMarginLeft(Length(intrinsicMargin, Fixed)); - if (style.marginRight().hasQuirk()) + if (style.marginRight().quirk()) style.setMarginRight(Length(intrinsicMargin, Fixed)); } if (style.height().isAuto()) { - if (style.marginTop().hasQuirk()) + if (style.marginTop().quirk()) style.setMarginTop(Length(intrinsicMargin, Fixed)); - if (style.marginBottom().hasQuirk()) + if (style.marginBottom().quirk()) style.setMarginBottom(Length(intrinsicMargin, Fixed)); } } @@ -674,10 +1124,7 @@ static EDisplay equivalentBlockDisplay(EDisplay display, bool isFloating, bool s case TABLE: case BOX: case FLEX: - case WEBKIT_FLEX: -#if ENABLE(CSS_GRID_LAYOUT) case GRID: -#endif return display; case LIST_ITEM: @@ -690,14 +1137,12 @@ static EDisplay equivalentBlockDisplay(EDisplay display, bool isFloating, bool s case INLINE_BOX: return BOX; case INLINE_FLEX: - case WEBKIT_INLINE_FLEX: return FLEX; -#if ENABLE(CSS_GRID_LAYOUT) case INLINE_GRID: return GRID; -#endif case INLINE: + case RUN_IN: case COMPACT: case INLINE_BLOCK: case TABLE_ROW_GROUP: @@ -718,15 +1163,20 @@ static EDisplay equivalentBlockDisplay(EDisplay display, bool isFloating, bool s } // CSS requires text-decoration to be reset at each DOM element for tables, -// inline blocks, inline tables, shadow DOM crossings, floating elements, +// inline blocks, inline tables, run-ins, shadow DOM crossings, floating elements, // and absolute or relatively positioned elements. static bool doesNotInheritTextDecoration(const RenderStyle& style, Element* e) { - return style.display() == TABLE || style.display() == INLINE_TABLE + return style.display() == TABLE || style.display() == INLINE_TABLE || style.display() == RUN_IN || style.display() == INLINE_BLOCK || style.display() == INLINE_BOX || isAtShadowBoundary(e) || style.isFloating() || style.hasOutOfFlowPosition(); } +static bool isDisplayFlexibleBox(EDisplay display) +{ + return display == FLEX || display == INLINE_FLEX; +} + #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) static bool isScrollableOverflow(EOverflow overflow) { @@ -734,16 +1184,6 @@ static bool isScrollableOverflow(EOverflow overflow) } #endif -void StyleResolver::adjustStyleForInterCharacterRuby() -{ - RenderStyle* style = m_state.style(); - if (style->rubyPosition() != RubyPositionInterCharacter || !m_state.element() || !m_state.element()->hasTagName(rtTag)) - return; - style->setTextAlign(CENTER); - if (style->isHorizontalWritingMode()) - style->setWritingMode(LeftToRightWritingMode); -} - void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& parentStyle, Element *e) { // Cache our original display. @@ -758,7 +1198,7 @@ void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& par if (e->hasTagName(tdTag)) { style.setDisplay(TABLE_CELL); style.setFloating(NoFloat); - } else if (is<HTMLTableElement>(*e)) + } else if (isHTMLTableElement(e)) style.setDisplay(style.isDisplayInlineType() ? INLINE_TABLE : TABLE); } @@ -775,7 +1215,7 @@ void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& par } // Tables never support the -webkit-* values for text-align and will reset back to the default. - if (is<HTMLTableElement>(e) && (style.textAlign() == WEBKIT_LEFT || style.textAlign() == WEBKIT_CENTER || style.textAlign() == WEBKIT_RIGHT)) + if (e && isHTMLTableElement(e) && (style.textAlign() == WEBKIT_LEFT || style.textAlign() == WEBKIT_CENTER || style.textAlign() == WEBKIT_RIGHT)) style.setTextAlign(TASTART); // Frames and framesets never honor position:relative or position:absolute. This is necessary to @@ -829,14 +1269,14 @@ void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& par if (style.writingMode() != TopToBottomWritingMode && (style.display() == BOX || style.display() == INLINE_BOX)) style.setWritingMode(TopToBottomWritingMode); - if (parentStyle.isDisplayFlexibleOrGridBox()) { + if (isDisplayFlexibleBox(parentStyle.display())) { style.setFloating(NoFloat); style.setDisplay(equivalentBlockDisplay(style.display(), style.isFloating(), !document().inQuirksMode())); } } // Make sure our z-index value is only applied if the object is positioned. - if (style.position() == StaticPosition && !parentStyle.isDisplayFlexibleOrGridBox()) + if (style.position() == StaticPosition && !isDisplayFlexibleBox(parentStyle.display())) style.setHasAutoZIndex(); // Auto z-index becomes 0 for the root element and transparent objects. This prevents @@ -849,28 +1289,19 @@ void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& par || style.clipPath() || style.boxReflect() || style.hasFilter() -#if ENABLE(FILTERS_LEVEL_2) - || style.hasBackdropFilter() -#endif || style.hasBlendMode() - || style.hasIsolation() || style.position() == StickyPosition - || (style.position() == FixedPosition && documentSettings() && documentSettings()->fixedPositionCreatesStackingContext()) + || (style.position() == FixedPosition && e && e->document().page() && e->document().page()->settings().fixedPositionCreatesStackingContext()) || style.hasFlowFrom() - || style.willChangeCreatesStackingContext() )) style.setZIndex(0); // Textarea considers overflow visible as auto. - if (is<HTMLTextAreaElement>(e)) { + if (e && isHTMLTextAreaElement(e)) { style.setOverflowX(style.overflowX() == OVISIBLE ? OAUTO : style.overflowX()); style.setOverflowY(style.overflowY() == OVISIBLE ? OAUTO : style.overflowY()); } - // Disallow -webkit-user-modify on :pseudo and ::pseudo elements. - if (e && !e->shadowPseudoId().isNull()) - style.setUserModify(READ_ONLY); - if (doesNotInheritTextDecoration(style, e)) style.setTextDecorationsInEffect(style.textDecoration()); else @@ -927,10 +1358,10 @@ void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& par // Important: Intrinsic margins get added to controls before the theme has adjusted the style, since the theme will // alter fonts and heights/widths. - if (is<HTMLFormControlElement>(e) && style.fontSize() >= 11) { + if (e && e->isFormControlElement() && style.fontSize() >= 11) { // Don't apply intrinsic margins to image buttons. The designer knows how big the images are, // so we have to treat all image buttons as though they were explicitly sized. - if (!is<HTMLInputElement>(*e) || !downcast<HTMLInputElement>(*e).isImageButton()) + if (!isHTMLInputElement(e) || !toHTMLInputElement(e)->isImageButton()) addIntrinsicMargins(style); } @@ -945,14 +1376,28 @@ void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& par // FIXME: when dropping the -webkit prefix on transform-style, we should also have opacity < 1 cause flattening. if (style.preserves3D() && (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE - || style.hasFilter() -#if ENABLE(FILTERS_LEVEL_2) - || style.hasBackdropFilter() -#endif - || style.hasBlendMode())) + || style.hasFilter())) style.setTransformStyle3D(TransformStyle3DFlat); + // Seamless iframes behave like blocks. Map their display to inline-block when marked inline. + if (e && e->hasTagName(iframeTag) && style.display() == INLINE && toHTMLIFrameElement(e)->shouldDisplaySeamlessly()) + style.setDisplay(INLINE_BLOCK); + + adjustGridItemPosition(style, parentStyle); + +#if ENABLE(SVG) if (e && e->isSVGElement()) { + // Spec: http://www.w3.org/TR/SVG/masking.html#OverflowProperty + if (style.overflowY() == OSCROLL) + style.setOverflowY(OHIDDEN); + else if (style.overflowY() == OAUTO) + style.setOverflowY(OVISIBLE); + + if (style.overflowX() == OSCROLL) + style.setOverflowX(OHIDDEN); + else if (style.overflowX() == OAUTO) + style.setOverflowX(OVISIBLE); + // Only the root <svg> element in an SVG document fragment tree honors css position if (!(e->hasTagName(SVGNames::svgTag) && e->parentNode() && !e->parentNode()->isSVGElement())) style.setPosition(RenderStyle::initialPosition()); @@ -966,15 +1411,45 @@ void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& par if ((e->hasTagName(SVGNames::foreignObjectTag) || e->hasTagName(SVGNames::textTag)) && style.isDisplayInlineType()) style.setDisplay(BLOCK); } +#endif +} + +void StyleResolver::adjustGridItemPosition(RenderStyle& style, const RenderStyle& parentStyle) const +{ + const GridPosition& columnStartPosition = style.gridItemColumnStart(); + const GridPosition& columnEndPosition = style.gridItemColumnEnd(); + const GridPosition& rowStartPosition = style.gridItemRowStart(); + const GridPosition& rowEndPosition = style.gridItemRowEnd(); + + // If opposing grid-placement properties both specify a grid span, they both compute to ‘auto’. + if (columnStartPosition.isSpan() && columnEndPosition.isSpan()) { + style.setGridItemColumnStart(GridPosition()); + style.setGridItemColumnEnd(GridPosition()); + } + + if (rowStartPosition.isSpan() && rowEndPosition.isSpan()) { + style.setGridItemRowStart(GridPosition()); + style.setGridItemRowEnd(GridPosition()); + } + + // Unknown named grid area compute to 'auto'. + const NamedGridAreaMap& map = parentStyle.namedGridArea(); + +#define CLEAR_UNKNOWN_NAMED_AREA(prop, Prop) \ + if (prop.isNamedGridArea() && !map.contains(prop.namedGridLine())) \ + style.setGridItem##Prop(GridPosition()); - // If the inherited value of justify-items includes the legacy keyword, 'auto' - // computes to the the inherited value. - if (parentStyle.justifyItemsPositionType() == LegacyPosition && style.justifyItemsPosition() == ItemPositionAuto) - style.setJustifyItems(parentStyle.justifyItems()); + CLEAR_UNKNOWN_NAMED_AREA(columnStartPosition, ColumnStart); + CLEAR_UNKNOWN_NAMED_AREA(columnEndPosition, ColumnEnd); + CLEAR_UNKNOWN_NAMED_AREA(rowStartPosition, RowStart); + CLEAR_UNKNOWN_NAMED_AREA(rowEndPosition, RowEnd); } bool StyleResolver::checkRegionStyle(Element* regionElement) { + // FIXME (BUG 72472): We don't add @-webkit-region rules of scoped style sheets for the moment, + // so all region rules are global by default. Verify whether that can stand or needs changing. + unsigned rulesSize = m_ruleSets.authorStyle()->regionSelectorsAndRuleSets().size(); for (unsigned i = 0; i < rulesSize; ++i) { ASSERT(m_ruleSets.authorStyle()->regionSelectorsAndRuleSets().at(i).ruleSet.get()); @@ -998,13 +1473,13 @@ static void checkForOrientationChange(RenderStyle* style) { FontOrientation fontOrientation; NonCJKGlyphOrientation glyphOrientation; - std::tie(fontOrientation, glyphOrientation) = style->fontAndGlyphOrientation(); + style->getFontAndGlyphOrientation(fontOrientation, glyphOrientation); - const auto& fontDescription = style->fontDescription(); + const FontDescription& fontDescription = style->fontDescription(); if (fontDescription.orientation() == fontOrientation && fontDescription.nonCJKGlyphOrientation() == glyphOrientation) return; - auto newFontDescription = fontDescription; + FontDescription newFontDescription(fontDescription); newFontDescription.setNonCJKGlyphOrientation(glyphOrientation); newFontDescription.setOrientation(fontOrientation); style->setFontDescription(newFontDescription); @@ -1022,26 +1497,25 @@ void StyleResolver::updateFont() checkForGenericFamilyChange(style, m_state.parentStyle()); checkForZoomChange(style, m_state.parentStyle()); checkForOrientationChange(style); - style->fontCascade().update(&document().fontSelector()); - if (m_state.fontSizeHasViewportUnits()) - style->setHasViewportUnits(true); + style->font().update(m_fontSelector); m_state.setFontDirty(false); } -Vector<RefPtr<StyleRule>> StyleResolver::styleRulesForElement(Element* e, unsigned rulesToInclude) +Vector<RefPtr<StyleRuleBase>> StyleResolver::styleRulesForElement(Element* e, unsigned rulesToInclude) { return pseudoStyleRulesForElement(e, NOPSEUDO, rulesToInclude); } -Vector<RefPtr<StyleRule>> StyleResolver::pseudoStyleRulesForElement(Element* element, PseudoId pseudoId, unsigned rulesToInclude) +Vector<RefPtr<StyleRuleBase>> StyleResolver::pseudoStyleRulesForElement(Element* e, PseudoId pseudoId, unsigned rulesToInclude) { - if (!element || !element->document().haveStylesheetsLoaded()) - return Vector<RefPtr<StyleRule>>(); + if (!e || !e->document().haveStylesheetsLoaded()) + return Vector<RefPtr<StyleRuleBase>>(); - m_state = State(*element, nullptr); + initElement(e); + m_state.initForStyleResolve(document(), e, 0); - ElementRuleCollector collector(*element, nullptr, m_ruleSets, m_state.selectorFilter()); - collector.setMode(SelectorChecker::Mode::CollectingRules); + ElementRuleCollector collector(this, m_state); + collector.setMode(SelectorChecker::CollectingRules); collector.setPseudoStyleRequest(PseudoStyleRequest(pseudoId)); collector.setMedium(m_medium.get()); @@ -1064,15 +1538,22 @@ Vector<RefPtr<StyleRule>> StyleResolver::pseudoStyleRulesForElement(Element* ele return collector.matchedRuleList(); } +// ------------------------------------------------------------------------------------- +// this is mostly boring stuff on how to apply a certain rule to the renderstyle... + +Length StyleResolver::convertToIntLength(const CSSPrimitiveValue* primitiveValue, const RenderStyle* style, const RenderStyle* rootStyle, double multiplier) +{ + return primitiveValue ? primitiveValue->convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion | FractionConversion | ViewportPercentageConversion>(style, rootStyle, multiplier) : Length(Undefined); +} + +Length StyleResolver::convertToFloatLength(const CSSPrimitiveValue* primitiveValue, const RenderStyle* style, const RenderStyle* rootStyle, double multiplier) +{ + return primitiveValue ? primitiveValue->convertToLength<FixedFloatConversion | PercentConversion | CalculatedConversion | FractionConversion | ViewportPercentageConversion>(style, rootStyle, multiplier) : Length(Undefined); +} + static bool shouldApplyPropertyInParseOrder(CSSPropertyID propertyID) { switch (propertyID) { - case CSSPropertyWebkitBackgroundClip: - case CSSPropertyBackgroundClip: - case CSSPropertyWebkitBackgroundOrigin: - case CSSPropertyBackgroundOrigin: - case CSSPropertyWebkitBackgroundSize: - case CSSPropertyBackgroundSize: case CSSPropertyWebkitBorderImage: case CSSPropertyBorderImage: case CSSPropertyBorderImageSlice: @@ -1080,8 +1561,6 @@ static bool shouldApplyPropertyInParseOrder(CSSPropertyID propertyID) case CSSPropertyBorderImageOutset: case CSSPropertyBorderImageRepeat: case CSSPropertyBorderImageWidth: - case CSSPropertyWebkitBoxShadow: - case CSSPropertyBoxShadow: case CSSPropertyWebkitTextDecoration: case CSSPropertyWebkitTextDecorationLine: case CSSPropertyWebkitTextDecorationStyle: @@ -1144,18 +1623,18 @@ const StyleResolver::MatchedPropertiesCacheItem* StyleResolver::findFromMatchedP MatchedPropertiesCache::iterator it = m_matchedPropertiesCache.find(hash); if (it == m_matchedPropertiesCache.end()) - return nullptr; + return 0; MatchedPropertiesCacheItem& cacheItem = it->value; - size_t size = matchResult.matchedProperties().size(); + size_t size = matchResult.matchedProperties.size(); if (size != cacheItem.matchedProperties.size()) - return nullptr; + return 0; for (size_t i = 0; i < size; ++i) { - if (matchResult.matchedProperties()[i] != cacheItem.matchedProperties[i]) - return nullptr; + if (matchResult.matchedProperties[i] != cacheItem.matchedProperties[i]) + return 0; } if (cacheItem.ranges != matchResult.ranges) - return nullptr; + return 0; return &cacheItem; } @@ -1170,13 +1649,13 @@ void StyleResolver::addToMatchedPropertiesCache(const RenderStyle* style, const ASSERT(hash); MatchedPropertiesCacheItem cacheItem; - cacheItem.matchedProperties.appendVector(matchResult.matchedProperties()); + cacheItem.matchedProperties.appendVector(matchResult.matchedProperties); cacheItem.ranges = matchResult.ranges; // Note that we don't cache the original RenderStyle instance. It may be further modified. // The RenderStyle in the cache is really just a holder for the substructures and never used as-is. cacheItem.renderStyle = RenderStyle::clone(style); cacheItem.parentRenderStyle = RenderStyle::clone(parentStyle); - m_matchedPropertiesCache.add(hash, WTFMove(cacheItem)); + m_matchedPropertiesCache.add(hash, std::move(cacheItem)); } void StyleResolver::invalidateMatchedPropertiesCache() @@ -1184,22 +1663,10 @@ void StyleResolver::invalidateMatchedPropertiesCache() m_matchedPropertiesCache.clear(); } -void StyleResolver::clearCachedPropertiesAffectedByViewportUnits() +static bool isCacheableInMatchedPropertiesCache(const Element* element, const RenderStyle* style, const RenderStyle* parentStyle) { - Vector<unsigned, 16> toRemove; - for (auto& cacheKeyValue : m_matchedPropertiesCache) { - if (cacheKeyValue.value.renderStyle->hasViewportUnits()) - toRemove.append(cacheKeyValue.key); - } - for (auto key : toRemove) - m_matchedPropertiesCache.remove(key); -} - -static bool isCacheableInMatchedPropertiesCache(const Element& element, const RenderStyle* style, const RenderStyle* parentStyle) -{ - // FIXME: Writing mode and direction properties modify state when applying to document element by calling - // Document::setWritingMode/DirectionSetOnDocumentElement. We can't skip the applying by caching. - if (&element == element.document().documentElement()) + // FIXME: CSSPropertyWebkitWritingMode modifies state when applying to document element. We can't skip the applying by caching. + if (element == element->document().documentElement() && element->document().writingModeSetOnDocumentElement()) return false; if (style->unique() || (style->styleType() != NOPSEUDO && parentStyle->unique())) return false; @@ -1207,7 +1674,7 @@ static bool isCacheableInMatchedPropertiesCache(const Element& element, const Re return false; if (style->zoom() != RenderStyle::initialZoom()) return false; - if (style->writingMode() != RenderStyle::initialWritingMode() || style->direction() != RenderStyle::initialDirection()) + if (style->writingMode() != RenderStyle::initialWritingMode()) return false; // The cache assumes static knowledge about which properties are inherited. if (parentStyle->hasExplicitlyInheritedProperties()) @@ -1223,7 +1690,7 @@ void extractDirectionAndWritingMode(const RenderStyle& style, const StyleResolve bool hadImportantWebkitWritingMode = false; bool hadImportantDirection = false; - for (const auto& matchedProperties : matchResult.matchedProperties()) { + for (auto& matchedProperties : matchResult.matchedProperties) { for (unsigned i = 0, count = matchedProperties.properties->propertyCount(); i < count; ++i) { auto property = matchedProperties.properties->propertyAt(i); if (!property.value()->isPrimitiveValue()) @@ -1231,13 +1698,13 @@ void extractDirectionAndWritingMode(const RenderStyle& style, const StyleResolve switch (property.id()) { case CSSPropertyWebkitWritingMode: if (!hadImportantWebkitWritingMode || property.isImportant()) { - writingMode = downcast<CSSPrimitiveValue>(*property.value()); + writingMode = toCSSPrimitiveValue(*property.value()); hadImportantWebkitWritingMode = property.isImportant(); } break; case CSSPropertyDirection: if (!hadImportantDirection || property.isImportant()) { - direction = downcast<CSSPrimitiveValue>(*property.value()); + direction = toCSSPrimitiveValue(*property.value()); hadImportantDirection = property.isImportant(); } break; @@ -1248,19 +1715,19 @@ void extractDirectionAndWritingMode(const RenderStyle& style, const StyleResolve } } -void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const Element& element, ShouldUseMatchedPropertiesCache shouldUseMatchedPropertiesCache) +void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const Element* element, ShouldUseMatchedPropertiesCache shouldUseMatchedPropertiesCache) { + ASSERT(element); State& state = m_state; - unsigned cacheHash = shouldUseMatchedPropertiesCache && matchResult.isCacheable ? computeMatchedPropertiesHash(matchResult.matchedProperties().data(), matchResult.matchedProperties().size()) : 0; + unsigned cacheHash = shouldUseMatchedPropertiesCache && matchResult.isCacheable ? computeMatchedPropertiesHash(matchResult.matchedProperties.data(), matchResult.matchedProperties.size()) : 0; bool applyInheritedOnly = false; - const MatchedPropertiesCacheItem* cacheItem = nullptr; - if (cacheHash && (cacheItem = findFromMatchedPropertiesCache(cacheHash, matchResult)) - && isCacheableInMatchedPropertiesCache(element, state.style(), state.parentStyle())) { + const MatchedPropertiesCacheItem* cacheItem = 0; + if (cacheHash && (cacheItem = findFromMatchedPropertiesCache(cacheHash, matchResult))) { // We can build up the style by copying non-inherited properties from an earlier style object built using the same exact // style declarations. We then only need to apply the inherited properties, if any, as their values can depend on the // element context. This is fast and saves memory by reusing the style data structures. state.style()->copyNonInheritedFrom(cacheItem->renderStyle.get()); - if (state.parentStyle()->inheritedDataShared(cacheItem->parentRenderStyle.get()) && !isAtShadowBoundary(&element)) { + if (state.parentStyle()->inheritedDataShared(cacheItem->parentRenderStyle.get()) && !isAtShadowBoundary(element)) { EInsideLink linkStatus = state.style()->insideLink(); // If the cache item parent style has identical inherited properties to the current parent style then the // resulting style will be identical too. We copy the inherited properties over from the cache and are done. @@ -1284,60 +1751,53 @@ void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const // Find out if there's a -webkit-appearance property in effect from the UA sheet. // If so, we cache the border and background styles so that RenderTheme::adjustStyle() // can look at them later to figure out if this is a styled form control or not. + state.setLineHeightValue(nullptr); CascadedProperties cascade(direction, writingMode); - cascade.addMatches(matchResult, false, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly); - cascade.addMatches(matchResult, true, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly); + if (!cascade.addMatches(matchResult, false, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly) + || !cascade.addMatches(matchResult, true, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly)) + return applyMatchedProperties(matchResult, element, DoNotUseMatchedPropertiesCache); - applyCascadedProperties(cascade, CSSPropertyWebkitRubyPosition, CSSPropertyWebkitRubyPosition, &matchResult); - adjustStyleForInterCharacterRuby(); - - // Resolve custom variables first. - applyCascadedProperties(cascade, CSSPropertyCustom, CSSPropertyCustom, &matchResult); - - // Start by applying properties that other properties may depend on. - applyCascadedProperties(cascade, firstCSSProperty, lastHighPriorityProperty, &matchResult); - + applyCascadedProperties(cascade, firstCSSProperty, CSSPropertyLineHeight); updateFont(); - applyCascadedProperties(cascade, firstLowPriorityProperty, lastCSSProperty, &matchResult); + applyCascadedProperties(cascade, CSSPropertyBackground, lastCSSProperty); state.cacheBorderAndBackground(); } CascadedProperties cascade(direction, writingMode); - cascade.addMatches(matchResult, false, 0, matchResult.matchedProperties().size() - 1, applyInheritedOnly); - cascade.addMatches(matchResult, true, matchResult.ranges.firstAuthorRule, matchResult.ranges.lastAuthorRule, applyInheritedOnly); - cascade.addMatches(matchResult, true, matchResult.ranges.firstUserRule, matchResult.ranges.lastUserRule, applyInheritedOnly); - cascade.addMatches(matchResult, true, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly); - - // Resolve custom properties first. - applyCascadedProperties(cascade, CSSPropertyCustom, CSSPropertyCustom, &matchResult); + if (!cascade.addMatches(matchResult, false, 0, matchResult.matchedProperties.size() - 1, applyInheritedOnly) + || !cascade.addMatches(matchResult, true, matchResult.ranges.firstAuthorRule, matchResult.ranges.lastAuthorRule, applyInheritedOnly) + || !cascade.addMatches(matchResult, true, matchResult.ranges.firstUserRule, matchResult.ranges.lastUserRule, applyInheritedOnly) + || !cascade.addMatches(matchResult, true, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly)) + return applyMatchedProperties(matchResult, element, DoNotUseMatchedPropertiesCache); - applyCascadedProperties(cascade, CSSPropertyWebkitRubyPosition, CSSPropertyWebkitRubyPosition, &matchResult); - - // Adjust the font size to be smaller if ruby-position is inter-character. - adjustStyleForInterCharacterRuby(); + state.setLineHeightValue(nullptr); // Start by applying properties that other properties may depend on. - applyCascadedProperties(cascade, firstCSSProperty, lastHighPriorityProperty, &matchResult); + applyCascadedProperties(cascade, firstCSSProperty, CSSPropertyLineHeight); // If the effective zoom value changes, we can't use the matched properties cache. Start over. if (cacheItem && cacheItem->renderStyle->effectiveZoom() != state.style()->effectiveZoom()) return applyMatchedProperties(matchResult, element, DoNotUseMatchedPropertiesCache); - // If our font got dirtied, update it now. + // If our font got dirtied, go ahead and update it now. updateFont(); + // Line-height is set when we are sure we decided on the font-size. + if (state.lineHeightValue()) + applyProperty(CSSPropertyLineHeight, state.lineHeightValue()); + // If the font changed, we can't use the matched properties cache. Start over. if (cacheItem && cacheItem->renderStyle->fontDescription() != state.style()->fontDescription()) return applyMatchedProperties(matchResult, element, DoNotUseMatchedPropertiesCache); // Apply properties that no other properties depend on. - applyCascadedProperties(cascade, firstLowPriorityProperty, lastCSSProperty, &matchResult); + applyCascadedProperties(cascade, CSSPropertyBackground, lastCSSProperty); // Finally, some properties must be applied in the order they were parsed. // There are some CSS properties that affect the same RenderStyle values, // so to preserve behavior, we queue them up during cascade and flush here. - cascade.applyDeferredProperties(*this, &matchResult); + cascade.applyDeferredProperties(*this); // Start loading resources referenced by this style. loadPendingResources(); @@ -1346,15 +1806,15 @@ void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const if (cacheItem || !cacheHash) return; - if (!isCacheableInMatchedPropertiesCache(*state.element(), state.style(), state.parentStyle())) + if (!isCacheableInMatchedPropertiesCache(state.element(), state.style(), state.parentStyle())) return; addToMatchedPropertiesCache(state.style(), state.parentStyle(), cacheHash, matchResult); } void StyleResolver::applyPropertyToStyle(CSSPropertyID id, CSSValue* value, RenderStyle* style) { - m_state = State(); - m_state.setParentStyle(*style); + initElement(0); + m_state.initForStyleResolve(document(), 0, style); m_state.setStyle(*style); applyPropertyToCurrentStyle(id, value); } @@ -1375,13 +1835,15 @@ inline bool isValidVisitedLinkProperty(CSSPropertyID id) case CSSPropertyBorderBottomColor: case CSSPropertyColor: case CSSPropertyOutlineColor: - case CSSPropertyColumnRuleColor: + case CSSPropertyWebkitColumnRuleColor: case CSSPropertyWebkitTextDecorationColor: case CSSPropertyWebkitTextEmphasisColor: case CSSPropertyWebkitTextFillColor: case CSSPropertyWebkitTextStrokeColor: +#if ENABLE(SVG) case CSSPropertyFill: case CSSPropertyStroke: +#endif return true; default: break; @@ -1427,7 +1889,7 @@ inline bool StyleResolver::isValidCueStyleProperty(CSSPropertyID id) case CSSPropertyFontFamily: case CSSPropertyFontSize: case CSSPropertyFontStyle: - case CSSPropertyFontVariantCaps: + case CSSPropertyFontVariant: case CSSPropertyFontWeight: case CSSPropertyLineHeight: case CSSPropertyOpacity: @@ -1461,200 +1923,1219 @@ bool StyleResolver::useSVGZoomRules() return m_state.element() && m_state.element()->isSVGElement(); } -// Scale with/height properties on inline SVG root. -bool StyleResolver::useSVGZoomRulesForLength() +static bool createGridTrackBreadth(CSSPrimitiveValue* primitiveValue, const StyleResolver::State& state, GridLength& workingLength) { - return is<SVGElement>(m_state.element()) && !(is<SVGSVGElement>(*m_state.element()) && m_state.element()->parentNode()); + if (primitiveValue->getValueID() == CSSValueWebkitMinContent) { + workingLength = Length(MinContent); + return true; + } + + if (primitiveValue->getValueID() == CSSValueWebkitMaxContent) { + workingLength = Length(MaxContent); + return true; + } + + if (primitiveValue->isFlex()) { + // Fractional unit. + workingLength.setFlex(primitiveValue->getDoubleValue()); + return true; + } + + workingLength = primitiveValue->convertToLength<FixedIntegerConversion | PercentConversion | ViewportPercentageConversion | AutoConversion>(state.style(), state.rootElementStyle(), state.style()->effectiveZoom()); + if (workingLength.length().isUndefined()) + return false; + + if (primitiveValue->isLength()) + workingLength.length().setQuirk(primitiveValue->isQuirkValue()); + + return true; } -StyleResolver::CascadedProperties* StyleResolver::cascadedPropertiesForRollback(const MatchResult& matchResult) +static bool createGridTrackSize(CSSValue* value, GridTrackSize& trackSize, const StyleResolver::State& state) { - ASSERT(cascadeLevel() != UserAgentLevel); - - TextDirection direction; - WritingMode writingMode; - extractDirectionAndWritingMode(*state().style(), matchResult, direction, writingMode); + if (value->isPrimitiveValue()) { + CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); + GridLength workingLength; + if (!createGridTrackBreadth(primitiveValue, state, workingLength)) + return false; - if (cascadeLevel() == AuthorLevel) { - CascadedProperties* authorRollback = state().authorRollback(); - if (authorRollback) - return authorRollback; - - auto newAuthorRollback(std::make_unique<CascadedProperties>(direction, writingMode)); - - // This special rollback cascade contains UA rules and user rules but no author rules. - newAuthorRollback->addMatches(matchResult, false, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, false); - newAuthorRollback->addMatches(matchResult, false, matchResult.ranges.firstUserRule, matchResult.ranges.lastUserRule, false); - newAuthorRollback->addMatches(matchResult, true, matchResult.ranges.firstUserRule, matchResult.ranges.lastUserRule, false); - newAuthorRollback->addMatches(matchResult, true, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, false); - - state().setAuthorRollback(newAuthorRollback); - return state().authorRollback(); - } - - if (cascadeLevel() == UserLevel) { - CascadedProperties* userRollback = state().userRollback(); - if (userRollback) - return userRollback; - - auto newUserRollback(std::make_unique<CascadedProperties>(direction, writingMode)); - - // This special rollback cascade contains only UA rules. - newUserRollback->addMatches(matchResult, false, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, false); - newUserRollback->addMatches(matchResult, true, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, false); - - state().setUserRollback(newUserRollback); - return state().userRollback(); + trackSize.setLength(workingLength); + return true; } - - return nullptr; + + CSSFunctionValue* minmaxFunction = toCSSFunctionValue(value); + CSSValueList* arguments = minmaxFunction->arguments(); + ASSERT_WITH_SECURITY_IMPLICATION(arguments->length() == 2); + GridLength minTrackBreadth; + GridLength maxTrackBreadth; + if (!createGridTrackBreadth(toCSSPrimitiveValue(arguments->itemWithoutBoundsCheck(0)), state, minTrackBreadth) || !createGridTrackBreadth(toCSSPrimitiveValue(arguments->itemWithoutBoundsCheck(1)), state, maxTrackBreadth)) + return false; + + trackSize.setMinMax(minTrackBreadth, maxTrackBreadth); + return true; } -void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, SelectorChecker::LinkMatchMask linkMatchMask, const MatchResult* matchResult) +static bool createGridTrackList(CSSValue* value, Vector<GridTrackSize>& trackSizes, NamedGridLinesMap& namedGridLines, const StyleResolver::State& state) { - ASSERT_WITH_MESSAGE(!isShorthandCSSProperty(id), "Shorthand property id = %d wasn't expanded at parsing time", id); + // Handle 'none'. + if (value->isPrimitiveValue()) { + CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); + return primitiveValue->getValueID() == CSSValueNone; + } - State& state = m_state; - - RefPtr<CSSValue> valueToApply = value; - if (value->isVariableDependentValue()) { - valueToApply = resolvedVariableValue(id, *downcast<CSSVariableDependentValue>(value)); - if (!valueToApply) { - if (CSSProperty::isInheritedProperty(id)) - valueToApply = CSSValuePool::singleton().createInheritedValue(); - else - valueToApply = CSSValuePool::singleton().createExplicitInitialValue(); + if (!value->isValueList()) + return false; + + size_t currentNamedGridLine = 0; + for (CSSValueListIterator i = value; i.hasMore(); i.advance()) { + CSSValue* currValue = i.value(); + if (currValue->isPrimitiveValue()) { + CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(currValue); + if (primitiveValue->isString()) { + NamedGridLinesMap::AddResult result = namedGridLines.add(primitiveValue->getStringValue(), Vector<size_t>()); + result.iterator->value.append(currentNamedGridLine); + continue; + } } + + ++currentNamedGridLine; + GridTrackSize trackSize; + if (!createGridTrackSize(currValue, trackSize, state)) + return false; + + trackSizes.append(trackSize); + } + + // The parser should have rejected any <track-list> without any <track-size> as + // this is not conformant to the syntax. + ASSERT(!trackSizes.isEmpty()); + return true; +} + + +static bool createGridPosition(CSSValue* value, GridPosition& position) +{ + // We accept the specification's grammar: + // 'auto' | [ <integer> || <string> ] | [ span && [ <integer> || string ] ] | <ident> + if (value->isPrimitiveValue()) { + CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); + // We translate <ident> to <string> during parsing as it makes handling it simpler. + if (primitiveValue->isString()) { + position.setNamedGridArea(primitiveValue->getStringValue()); + return true; + } + + ASSERT(primitiveValue->getValueID() == CSSValueAuto); + return true; + } + + CSSValueList* values = toCSSValueList(value); + ASSERT(values->length()); + + bool isSpanPosition = false; + // The specification makes the <integer> optional, in which case it default to '1'. + int gridLineNumber = 1; + String gridLineName; + + CSSValueListIterator it = values; + CSSPrimitiveValue* currentValue = toCSSPrimitiveValue(it.value()); + if (currentValue->getValueID() == CSSValueSpan) { + isSpanPosition = true; + it.advance(); + currentValue = it.hasMore() ? toCSSPrimitiveValue(it.value()) : 0; } + if (currentValue && currentValue->isNumber()) { + gridLineNumber = currentValue->getIntValue(); + it.advance(); + currentValue = it.hasMore() ? toCSSPrimitiveValue(it.value()) : 0; + } + + if (currentValue && currentValue->isString()) { + gridLineName = currentValue->getStringValue(); + it.advance(); + } + + ASSERT(!it.hasMore()); + if (isSpanPosition) + position.setSpanPosition(gridLineNumber, gridLineName); + else + position.setExplicitPosition(gridLineNumber, gridLineName); + + return true; +} + +void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value) +{ + ASSERT_WITH_MESSAGE(!isExpandedShorthand(id), "Shorthand property id = %d wasn't expanded at parsing time", id); + + State& state = m_state; + if (CSSProperty::isDirectionAwareProperty(id)) { CSSPropertyID newId = CSSProperty::resolveDirectionAwareProperty(id, state.style()->direction(), state.style()->writingMode()); ASSERT(newId != id); - return applyProperty(newId, valueToApply.get(), linkMatchMask, matchResult); + return applyProperty(newId, value); } - - CSSValue* valueToCheckForInheritInitial = valueToApply.get(); - CSSCustomPropertyValue* customPropertyValue = nullptr; - - if (id == CSSPropertyCustom) { - customPropertyValue = &downcast<CSSCustomPropertyValue>(*valueToApply); - valueToCheckForInheritInitial = customPropertyValue->value().get(); + + bool isInherit = state.parentNode() && value->isInheritedValue(); + bool isInitial = value->isInitialValue() || (!state.parentNode() && value->isInheritedValue()); + + ASSERT(!isInherit || !isInitial); // isInherit -> !isInitial && isInitial -> !isInherit + ASSERT(!isInherit || (state.parentNode() && state.parentStyle())); // isInherit -> (state.parentNode() && state.parentStyle()) + + if (!state.applyPropertyToRegularStyle() && (!state.applyPropertyToVisitedLinkStyle() || !isValidVisitedLinkProperty(id))) { + // Limit the properties that can be applied to only the ones honored by :visited. + return; } - bool isInherit = state.parentStyle() && valueToCheckForInheritInitial->isInheritedValue(); - bool isInitial = valueToCheckForInheritInitial->isInitialValue() || (!state.parentStyle() && valueToCheckForInheritInitial->isInheritedValue()); - - bool isUnset = valueToCheckForInheritInitial->isUnsetValue(); - bool isRevert = valueToCheckForInheritInitial->isRevertValue(); - - if (isRevert) { - if (cascadeLevel() == UserAgentLevel || !matchResult) - isUnset = true; - else { - // Fetch the correct rollback object from the state, building it if necessary. - // This requires having the original MatchResult available. - auto* rollback = cascadedPropertiesForRollback(*matchResult); - ASSERT(rollback); - - // With the cascade built, we need to obtain the property and apply it. If the property is - // not present, then we behave like "unset." Otherwise we apply the property instead of - // our own. - if (customPropertyValue) { - if (rollback->hasCustomProperty(customPropertyValue->name())) { - auto property = rollback->customProperty(customPropertyValue->name()); - if (property.cssValue[linkMatchMask]) - applyProperty(property.id, property.cssValue[linkMatchMask], linkMatchMask, matchResult); - return; - } - } else if (rollback->hasProperty(id)) { - auto& property = rollback->property(id); - if (property.cssValue[linkMatchMask]) - applyProperty(property.id, property.cssValue[linkMatchMask], linkMatchMask, matchResult); + if (isInherit && !state.parentStyle()->hasExplicitlyInheritedProperties() && !CSSProperty::isInheritedProperty(id)) + state.parentStyle()->setHasExplicitlyInheritedProperties(); + + // Check lookup table for implementations and use when available. + const PropertyHandler& handler = m_deprecatedStyleBuilder.propertyHandler(id); + if (handler.isValid()) { + if (isInherit) + handler.applyInheritValue(id, this); + else if (isInitial) + handler.applyInitialValue(id, this); + else + handler.applyValue(id, this, value); + return; + } + + CSSPrimitiveValue* primitiveValue = value->isPrimitiveValue() ? toCSSPrimitiveValue(value) : 0; + + float zoomFactor = state.style()->effectiveZoom(); + + // What follows is a list that maps the CSS properties into their corresponding front-end + // RenderStyle values. + switch (id) { + // lists + case CSSPropertyContent: + // list of string, uri, counter, attr, i + { + // FIXME: In CSS3, it will be possible to inherit content. In CSS2 it is not. This + // note is a reminder that eventually "inherit" needs to be supported. + + if (isInitial) { + state.style()->clearContent(); return; } + + if (!value->isValueList()) + return; + + bool didSet = false; + for (CSSValueListIterator i = value; i.hasMore(); i.advance()) { + CSSValue* item = i.value(); + if (item->isImageGeneratorValue()) { + if (item->isGradientValue()) + state.style()->setContent(StyleGeneratedImage::create(*toCSSGradientValue(item)->gradientWithStylesResolved(this)), didSet); + else + state.style()->setContent(StyleGeneratedImage::create(*toCSSImageGeneratorValue(item)), didSet); + didSet = true; +#if ENABLE(CSS_IMAGE_SET) + } else if (item->isImageSetValue()) { + state.style()->setContent(setOrPendingFromValue(CSSPropertyContent, toCSSImageSetValue(item)), didSet); + didSet = true; +#endif + } + + if (item->isImageValue()) { + state.style()->setContent(cachedOrPendingFromValue(CSSPropertyContent, toCSSImageValue(item)), didSet); + didSet = true; + continue; + } + + if (!item->isPrimitiveValue()) + continue; + + CSSPrimitiveValue* contentValue = toCSSPrimitiveValue(item); + + if (contentValue->isString()) { + state.style()->setContent(contentValue->getStringValue().impl(), didSet); + didSet = true; + } else if (contentValue->isAttr()) { + // FIXME: Can a namespace be specified for an attr(foo)? + if (state.style()->styleType() == NOPSEUDO) + state.style()->setUnique(); + else + state.parentStyle()->setUnique(); + QualifiedName attr(nullAtom, contentValue->getStringValue().impl(), nullAtom); + const AtomicString& value = state.element()->getAttribute(attr); + state.style()->setContent(value.isNull() ? emptyAtom : value.impl(), didSet); + didSet = true; + // Register the fact that the attribute value affects the style. + m_ruleSets.features().attrsInRules.add(attr.localName().impl()); + } else if (contentValue->isCounter()) { + Counter* counterValue = contentValue->getCounterValue(); + EListStyleType listStyleType = NoneListStyle; + CSSValueID listStyleIdent = counterValue->listStyleIdent(); + if (listStyleIdent != CSSValueNone) + listStyleType = static_cast<EListStyleType>(listStyleIdent - CSSValueDisc); + auto counter = std::make_unique<CounterContent>(counterValue->identifier(), listStyleType, counterValue->separator()); + state.style()->setContent(std::move(counter), didSet); + didSet = true; + } else { + switch (contentValue->getValueID()) { + case CSSValueOpenQuote: + state.style()->setContent(OPEN_QUOTE, didSet); + didSet = true; + break; + case CSSValueCloseQuote: + state.style()->setContent(CLOSE_QUOTE, didSet); + didSet = true; + break; + case CSSValueNoOpenQuote: + state.style()->setContent(NO_OPEN_QUOTE, didSet); + didSet = true; + break; + case CSSValueNoCloseQuote: + state.style()->setContent(NO_CLOSE_QUOTE, didSet); + didSet = true; + break; + default: + // normal and none do not have any effect. + { } + } + } + } + if (!didSet) + state.style()->clearContent(); + return; + } + case CSSPropertyWebkitAlt: + { + bool didSet = false; + if (primitiveValue->isString()) { + state.style()->setContentAltText(primitiveValue->getStringValue().impl()); + didSet = true; + } else if (primitiveValue->isAttr()) { + // FIXME: Can a namespace be specified for an attr(foo)? + if (state.style()->styleType() == NOPSEUDO) + state.style()->setUnique(); + else + state.parentStyle()->setUnique(); + QualifiedName attr(nullAtom, primitiveValue->getStringValue().impl(), nullAtom); + const AtomicString& value = state.element()->getAttribute(attr); + state.style()->setContentAltText(value.isNull() ? emptyAtom : value.impl()); + didSet = true; + // Register the fact that the attribute value affects the style. + m_ruleSets.features().attrsInRules.add(attr.localName().impl()); + } + if (!didSet) + state.style()->setContentAltText(emptyAtom); + return; + } - isUnset = true; + case CSSPropertyQuotes: + if (isInherit) { + state.style()->setQuotes(state.parentStyle()->quotes()); + return; + } + if (isInitial) { + state.style()->setQuotes(0); + return; + } + if (value->isValueList()) { + CSSValueList* list = toCSSValueList(value); + Vector<std::pair<String, String>> quotes; + for (size_t i = 0; i < list->length(); i += 2) { + CSSValue* first = list->itemWithoutBoundsCheck(i); + // item() returns null if out of bounds so this is safe. + CSSValue* second = list->item(i + 1); + if (!second) + continue; + ASSERT_WITH_SECURITY_IMPLICATION(first->isPrimitiveValue()); + ASSERT_WITH_SECURITY_IMPLICATION(second->isPrimitiveValue()); + String startQuote = toCSSPrimitiveValue(first)->getStringValue(); + String endQuote = toCSSPrimitiveValue(second)->getStringValue(); + quotes.append(std::make_pair(startQuote, endQuote)); + } + state.style()->setQuotes(QuotesData::create(quotes)); + return; + } + if (primitiveValue) { + if (primitiveValue->getValueID() == CSSValueNone) + state.style()->setQuotes(QuotesData::create(Vector<std::pair<String, String>>())); } + return; + // Shorthand properties. + case CSSPropertyFont: + if (isInherit) { + FontDescription fontDescription = state.parentStyle()->fontDescription(); + state.style()->setLineHeight(state.parentStyle()->specifiedLineHeight()); + state.setLineHeightValue(0); + setFontDescription(fontDescription); + } else if (isInitial) { + Settings* settings = documentSettings(); + ASSERT(settings); // If we're doing style resolution, this document should always be in a frame and thus have settings + if (!settings) + return; + initializeFontStyle(settings); + } else if (primitiveValue) { + state.style()->setLineHeight(RenderStyle::initialLineHeight()); + state.setLineHeightValue(0); + + FontDescription fontDescription; + RenderTheme::defaultTheme()->systemFont(primitiveValue->getValueID(), fontDescription); + + // Double-check and see if the theme did anything. If not, don't bother updating the font. + if (fontDescription.isAbsoluteSize()) { + // Make sure the rendering mode and printer font settings are updated. + Settings* settings = documentSettings(); + ASSERT(settings); // If we're doing style resolution, this document should always be in a frame and thus have settings + if (!settings) + return; + fontDescription.setRenderingMode(settings->fontRenderingMode()); + fontDescription.setUsePrinterFont(document().printing() || !settings->screenFontSubstitutionEnabled()); + + // Handle the zoom factor. + fontDescription.setComputedSize(Style::computedFontSizeFromSpecifiedSize(fontDescription.specifiedSize(), fontDescription.isAbsoluteSize(), useSVGZoomRules(), state.style(), document())); + setFontDescription(fontDescription); + } + } else if (value->isFontValue()) { + CSSFontValue* font = toCSSFontValue(value); + if (!font->style || !font->variant || !font->weight + || !font->size || !font->lineHeight || !font->family) + return; + applyProperty(CSSPropertyFontStyle, font->style.get()); + applyProperty(CSSPropertyFontVariant, font->variant.get()); + applyProperty(CSSPropertyFontWeight, font->weight.get()); + // The previous properties can dirty our font but they don't try to read the font's + // properties back, which is safe. However if font-size is using the 'ex' unit, it will + // need query the dirtied font's x-height to get the computed size. To be safe in this + // case, let's just update the font now. + updateFont(); + applyProperty(CSSPropertyFontSize, font->size.get()); + + state.setLineHeightValue(font->lineHeight.get()); + + applyProperty(CSSPropertyFontFamily, font->family.get()); + } + return; + + case CSSPropertyBackground: + case CSSPropertyBackgroundPosition: + case CSSPropertyBackgroundRepeat: + case CSSPropertyBorder: + case CSSPropertyBorderBottom: + case CSSPropertyBorderColor: + case CSSPropertyBorderImage: + case CSSPropertyBorderLeft: + case CSSPropertyBorderRadius: + case CSSPropertyBorderRight: + case CSSPropertyBorderSpacing: + case CSSPropertyBorderStyle: + case CSSPropertyBorderTop: + case CSSPropertyBorderWidth: + case CSSPropertyListStyle: + case CSSPropertyMargin: + case CSSPropertyOutline: + case CSSPropertyOverflow: + case CSSPropertyPadding: + case CSSPropertyTransition: + case CSSPropertyWebkitAnimation: + case CSSPropertyWebkitBorderAfter: + case CSSPropertyWebkitBorderBefore: + case CSSPropertyWebkitBorderEnd: + case CSSPropertyWebkitBorderStart: + case CSSPropertyWebkitBorderRadius: + case CSSPropertyWebkitColumns: + case CSSPropertyWebkitColumnRule: + case CSSPropertyWebkitFlex: + case CSSPropertyWebkitFlexFlow: + case CSSPropertyWebkitGridArea: + case CSSPropertyWebkitGridColumn: + case CSSPropertyWebkitGridRow: + case CSSPropertyWebkitMarginCollapse: + case CSSPropertyWebkitMarquee: + case CSSPropertyWebkitMask: + case CSSPropertyWebkitMaskPosition: + case CSSPropertyWebkitMaskRepeat: + case CSSPropertyWebkitTextEmphasis: + case CSSPropertyWebkitTextStroke: + case CSSPropertyWebkitTransition: + case CSSPropertyWebkitTransformOrigin: + ASSERT(isExpandedShorthand(id)); + ASSERT_NOT_REACHED(); + break; + + // CSS3 Properties + case CSSPropertyTextShadow: + case CSSPropertyBoxShadow: + case CSSPropertyWebkitBoxShadow: { + if (isInherit) { + if (id == CSSPropertyTextShadow) + return state.style()->setTextShadow(state.parentStyle()->textShadow() ? adoptPtr(new ShadowData(*state.parentStyle()->textShadow())) : nullptr); + return state.style()->setBoxShadow(state.parentStyle()->boxShadow() ? adoptPtr(new ShadowData(*state.parentStyle()->boxShadow())) : nullptr); + } + if (isInitial || primitiveValue) // initial | none + return id == CSSPropertyTextShadow ? state.style()->setTextShadow(nullptr) : state.style()->setBoxShadow(nullptr); + + if (!value->isValueList()) + return; + + for (CSSValueListIterator i = value; i.hasMore(); i.advance()) { + CSSValue* currValue = i.value(); + if (!currValue->isShadowValue()) + continue; + CSSShadowValue* item = toCSSShadowValue(currValue); + int x = item->x->computeLength<int>(state.style(), state.rootElementStyle(), zoomFactor); + if (item->x->isViewportPercentageLength()) + x = viewportPercentageValue(*item->x, x); + int y = item->y->computeLength<int>(state.style(), state.rootElementStyle(), zoomFactor); + if (item->y->isViewportPercentageLength()) + y = viewportPercentageValue(*item->y, y); + int blur = item->blur ? item->blur->computeLength<int>(state.style(), state.rootElementStyle(), zoomFactor) : 0; + if (item->blur && item->blur->isViewportPercentageLength()) + blur = viewportPercentageValue(*item->blur, blur); + int spread = item->spread ? item->spread->computeLength<int>(state.style(), state.rootElementStyle(), zoomFactor) : 0; + if (item->spread && item->spread->isViewportPercentageLength()) + spread = viewportPercentageValue(*item->spread, spread); + ShadowStyle shadowStyle = item->style && item->style->getValueID() == CSSValueInset ? Inset : Normal; + Color color; + if (item->color) + color = colorFromPrimitiveValue(item->color.get()); + else if (state.style()) + color = state.style()->color(); + + OwnPtr<ShadowData> shadowData = adoptPtr(new ShadowData(IntPoint(x, y), blur, spread, shadowStyle, id == CSSPropertyWebkitBoxShadow, color.isValid() ? color : Color::transparent)); + if (id == CSSPropertyTextShadow) + state.style()->setTextShadow(shadowData.release(), i.index()); // add to the list if this is not the first entry + else + state.style()->setBoxShadow(shadowData.release(), i.index()); // add to the list if this is not the first entry + } + return; } - - if (isUnset) { - if (CSSProperty::isInheritedProperty(id)) - isInherit = true; + case CSSPropertyWebkitBoxReflect: { + HANDLE_INHERIT_AND_INITIAL(boxReflect, BoxReflect) + if (primitiveValue) { + state.style()->setBoxReflect(RenderStyle::initialBoxReflect()); + return; + } + + if (!value->isReflectValue()) + return; + + CSSReflectValue* reflectValue = toCSSReflectValue(value); + RefPtr<StyleReflection> reflection = StyleReflection::create(); + reflection->setDirection(*reflectValue->direction()); + if (reflectValue->offset()) + reflection->setOffset(reflectValue->offset()->convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion>(state.style(), state.rootElementStyle(), zoomFactor)); + NinePieceImage mask; + mask.setMaskDefaults(); + m_styleMap.mapNinePieceImage(id, reflectValue->mask(), mask); + reflection->setMask(mask); + + state.style()->setBoxReflect(reflection.release()); + return; + } + case CSSPropertySrc: // Only used in @font-face rules. + return; + case CSSPropertyUnicodeRange: // Only used in @font-face rules. + return; + case CSSPropertyWebkitLocale: { + HANDLE_INHERIT_AND_INITIAL(locale, Locale); + if (!primitiveValue) + return; + if (primitiveValue->getValueID() == CSSValueAuto) + state.style()->setLocale(nullAtom); else - isInitial = true; + state.style()->setLocale(primitiveValue->getStringValue()); + FontDescription fontDescription = state.style()->fontDescription(); + fontDescription.setScript(localeToScriptCodeForFontSelection(state.style()->locale())); + setFontDescription(fontDescription); + return; } +#if ENABLE(IOS_TEXT_AUTOSIZING) + case CSSPropertyWebkitTextSizeAdjust: { + HANDLE_INHERIT_AND_INITIAL(textSizeAdjust, TextSizeAdjust) + if (!primitiveValue) + return; - ASSERT(!isInherit || !isInitial); // isInherit -> !isInitial && isInitial -> !isInherit + if (primitiveValue->getValueID() == CSSValueAuto) + state.style()->setTextSizeAdjust(TextSizeAdjustment(AutoTextSizeAdjustment)); + else if (primitiveValue->getValueID() == CSSValueNone) + state.style()->setTextSizeAdjust(TextSizeAdjustment(NoTextSizeAdjustment)); + else + state.style()->setTextSizeAdjust(TextSizeAdjustment(primitiveValue->getFloatValue())); - if (!state.applyPropertyToRegularStyle() && (!state.applyPropertyToVisitedLinkStyle() || !isValidVisitedLinkProperty(id))) { - // Limit the properties that can be applied to only the ones honored by :visited. + state.setFontDirty(true); return; } +#endif +#if ENABLE(DASHBOARD_SUPPORT) + case CSSPropertyWebkitDashboardRegion: + { + HANDLE_INHERIT_AND_INITIAL(dashboardRegions, DashboardRegions) + if (!primitiveValue) + return; - if (isInherit && !state.parentStyle()->hasExplicitlyInheritedProperties() && !CSSProperty::isInheritedProperty(id)) - state.parentStyle()->setHasExplicitlyInheritedProperties(); - - if (id == CSSPropertyCustom) { - CSSCustomPropertyValue* customProperty = &downcast<CSSCustomPropertyValue>(*valueToApply); + if (primitiveValue->getValueID() == CSSValueNone) { + state.style()->setDashboardRegions(RenderStyle::noneDashboardRegions()); + return; + } + + DashboardRegion* region = primitiveValue->getDashboardRegionValue(); + if (!region) + return; + + DashboardRegion* first = region; + while (region) { + Length top = convertToIntLength(region->top(), state.style(), state.rootElementStyle()); + Length right = convertToIntLength(region->right(), state.style(), state.rootElementStyle()); + Length bottom = convertToIntLength(region->bottom(), state.style(), state.rootElementStyle()); + Length left = convertToIntLength(region->left(), state.style(), state.rootElementStyle()); + + if (top.isUndefined()) + top = Length(); + if (right.isUndefined()) + right = Length(); + if (bottom.isUndefined()) + bottom = Length(); + if (left.isUndefined()) + left = Length(); + + if (region->m_isCircle) + state.style()->setDashboardRegion(StyleDashboardRegion::Circle, region->m_label, top, right, bottom, left, region == first ? false : true); + else if (region->m_isRectangle) + state.style()->setDashboardRegion(StyleDashboardRegion::Rectangle, region->m_label, top, right, bottom, left, region == first ? false : true); + region = region->m_next.get(); + } + + state.document().setHasAnnotatedRegions(true); + + return; + } +#endif + case CSSPropertyWebkitTextStrokeWidth: { + HANDLE_INHERIT_AND_INITIAL(textStrokeWidth, TextStrokeWidth) + float width = 0; + switch (primitiveValue->getValueID()) { + case CSSValueThin: + case CSSValueMedium: + case CSSValueThick: { + double result = 1.0 / 48; + if (primitiveValue->getValueID() == CSSValueMedium) + result *= 3; + else if (primitiveValue->getValueID() == CSSValueThick) + result *= 5; + Ref<CSSPrimitiveValue> value(CSSPrimitiveValue::create(result, CSSPrimitiveValue::CSS_EMS)); + width = value.get().computeLength<float>(state.style(), state.rootElementStyle(), zoomFactor); + break; + } + default: + width = primitiveValue->computeLength<float>(state.style(), state.rootElementStyle(), zoomFactor); + break; + } + state.style()->setTextStrokeWidth(width); + return; + } + case CSSPropertyWebkitTransform: { + HANDLE_INHERIT_AND_INITIAL(transform, Transform); + TransformOperations operations; + transformsForValue(state.style(), state.rootElementStyle(), value, operations); + state.style()->setTransform(operations); + return; + } + case CSSPropertyWebkitPerspective: { + HANDLE_INHERIT_AND_INITIAL(perspective, Perspective) + + if (!primitiveValue) + return; + + if (primitiveValue->getValueID() == CSSValueNone) { + state.style()->setPerspective(0); + return; + } + + float perspectiveValue; + if (primitiveValue->isLength()) + perspectiveValue = primitiveValue->computeLength<float>(state.style(), state.rootElementStyle(), zoomFactor); + else if (primitiveValue->isNumber()) { + // For backward compatibility, treat valueless numbers as px. + Ref<CSSPrimitiveValue> value(CSSPrimitiveValue::create(primitiveValue->getDoubleValue(), CSSPrimitiveValue::CSS_PX)); + perspectiveValue = value.get().computeLength<float>(state.style(), state.rootElementStyle(), zoomFactor); + } else + return; + + if (perspectiveValue >= 0.0f) + state.style()->setPerspective(perspectiveValue); + return; + } +#if PLATFORM(IOS) + case CSSPropertyWebkitTouchCallout: { + HANDLE_INHERIT_AND_INITIAL(touchCalloutEnabled, TouchCalloutEnabled); + if (!primitiveValue) + break; + + state.style()->setTouchCalloutEnabled(primitiveValue->getStringValue().lower() != "none"); + return; + } + + // FIXME: CSSPropertyWebkitCompositionFillColor shouldn't be iOS-specific. Once we fix up its usage in + // InlineTextBox::paintCompositionBackground() we should move it outside the PLATFORM(IOS)-guard. + // See <https://bugs.webkit.org/show_bug.cgi?id=126296>. + case CSSPropertyWebkitCompositionFillColor: { + HANDLE_INHERIT_AND_INITIAL(compositionFillColor, CompositionFillColor); + if (!primitiveValue) + break; + state.style()->setCompositionFillColor(colorFromPrimitiveValue(primitiveValue)); + return; + } +#endif +#if ENABLE(TOUCH_EVENTS) + case CSSPropertyWebkitTapHighlightColor: { + HANDLE_INHERIT_AND_INITIAL(tapHighlightColor, TapHighlightColor); + if (!primitiveValue) + break; + + Color col = colorFromPrimitiveValue(primitiveValue); + state.style()->setTapHighlightColor(col); + return; + } +#endif +#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) + case CSSPropertyWebkitOverflowScrolling: { + HANDLE_INHERIT_AND_INITIAL(useTouchOverflowScrolling, UseTouchOverflowScrolling); + if (!primitiveValue) + break; + state.style()->setUseTouchOverflowScrolling(primitiveValue->getValueID() == CSSValueTouch); + return; + } +#endif + case CSSPropertyInvalid: + return; + case CSSPropertyFontStretch: + case CSSPropertyPage: + case CSSPropertyTextLineThrough: + case CSSPropertyTextLineThroughColor: + case CSSPropertyTextLineThroughMode: + case CSSPropertyTextLineThroughStyle: + case CSSPropertyTextLineThroughWidth: + case CSSPropertyTextOverline: + case CSSPropertyTextOverlineColor: + case CSSPropertyTextOverlineMode: + case CSSPropertyTextOverlineStyle: + case CSSPropertyTextOverlineWidth: + case CSSPropertyTextUnderline: + case CSSPropertyTextUnderlineColor: + case CSSPropertyTextUnderlineMode: + case CSSPropertyTextUnderlineStyle: + case CSSPropertyTextUnderlineWidth: + case CSSPropertyWebkitFontSizeDelta: + case CSSPropertyWebkitTextDecorationsInEffect: + return; + + // CSS Text Layout Module Level 3: Vertical writing support + case CSSPropertyWebkitWritingMode: { + HANDLE_INHERIT_AND_INITIAL(writingMode, WritingMode); + + if (primitiveValue) + setWritingMode(*primitiveValue); + + // FIXME: It is not ok to modify document state while applying style. + if (state.element() && state.element() == state.document().documentElement()) + state.document().setWritingModeSetOnDocumentElement(true); + return; + } + + case CSSPropertyWebkitTextOrientation: { + HANDLE_INHERIT_AND_INITIAL(textOrientation, TextOrientation); + + if (primitiveValue) + setTextOrientation(*primitiveValue); + + return; + } + + case CSSPropertyWebkitLineBoxContain: { + HANDLE_INHERIT_AND_INITIAL(lineBoxContain, LineBoxContain) + if (primitiveValue && primitiveValue->getValueID() == CSSValueNone) { + state.style()->setLineBoxContain(LineBoxContainNone); + return; + } + + if (!value->isLineBoxContainValue()) + return; + + state.style()->setLineBoxContain(toCSSLineBoxContainValue(value)->value()); + return; + } + + // CSS Fonts Module Level 3 + case CSSPropertyWebkitFontFeatureSettings: { + if (primitiveValue && primitiveValue->getValueID() == CSSValueNormal) { + setFontDescription(state.style()->fontDescription().makeNormalFeatureSettings()); + return; + } + + if (!value->isValueList()) + return; + + FontDescription fontDescription = state.style()->fontDescription(); + CSSValueList* list = toCSSValueList(value); + RefPtr<FontFeatureSettings> settings = FontFeatureSettings::create(); + int len = list->length(); + for (int i = 0; i < len; ++i) { + CSSValue* item = list->itemWithoutBoundsCheck(i); + if (!item->isFontFeatureValue()) + continue; + CSSFontFeatureValue* feature = toCSSFontFeatureValue(item); + settings->append(FontFeature(feature->tag(), feature->value())); + } + fontDescription.setFeatureSettings(settings.release()); + setFontDescription(fontDescription); + return; + } + +#if ENABLE(CSS_FILTERS) + case CSSPropertyWebkitFilter: { + HANDLE_INHERIT_AND_INITIAL(filter, Filter); + FilterOperations operations; + if (createFilterOperations(value, operations)) + state.style()->setFilter(operations); + return; + } +#endif + case CSSPropertyWebkitGridAutoColumns: { + HANDLE_INHERIT_AND_INITIAL(gridAutoColumns, GridAutoColumns); + GridTrackSize trackSize; + if (!createGridTrackSize(value, trackSize, state)) + return; + state.style()->setGridAutoColumns(trackSize); + return; + } + case CSSPropertyWebkitGridAutoRows: { + HANDLE_INHERIT_AND_INITIAL(gridAutoRows, GridAutoRows); + GridTrackSize trackSize; + if (!createGridTrackSize(value, trackSize, state)) + return; + state.style()->setGridAutoRows(trackSize); + return; + } + case CSSPropertyWebkitGridDefinitionColumns: { if (isInherit) { - RefPtr<CSSValue> customVal = state.parentStyle()->getCustomPropertyValue(customProperty->name()); - if (!customVal) - customVal = CSSCustomPropertyValue::createInvalid(); - state.style()->setCustomPropertyValue(customProperty->name(), customVal); - } else if (isInitial) - state.style()->setCustomPropertyValue(customProperty->name(), CSSCustomPropertyValue::createInvalid()); - else - state.style()->setCustomPropertyValue(customProperty->name(), customProperty->value()); + m_state.style()->setGridColumns(m_state.parentStyle()->gridColumns()); + m_state.style()->setNamedGridColumnLines(m_state.parentStyle()->namedGridColumnLines()); + return; + } + if (isInitial) { + m_state.style()->setGridColumns(RenderStyle::initialGridColumns()); + m_state.style()->setNamedGridColumnLines(RenderStyle::initialNamedGridColumnLines()); + return; + } + Vector<GridTrackSize> trackSizes; + NamedGridLinesMap namedGridLines; + if (!createGridTrackList(value, trackSizes, namedGridLines, state)) + return; + state.style()->setGridColumns(trackSizes); + state.style()->setNamedGridColumnLines(namedGridLines); + return; + } + case CSSPropertyWebkitGridDefinitionRows: { + if (isInherit) { + m_state.style()->setGridRows(m_state.parentStyle()->gridRows()); + m_state.style()->setNamedGridRowLines(m_state.parentStyle()->namedGridRowLines()); + return; + } + if (isInitial) { + m_state.style()->setGridRows(RenderStyle::initialGridRows()); + m_state.style()->setNamedGridRowLines(RenderStyle::initialNamedGridRowLines()); + return; + } + Vector<GridTrackSize> trackSizes; + NamedGridLinesMap namedGridLines; + if (!createGridTrackList(value, trackSizes, namedGridLines, state)) + return; + state.style()->setGridRows(trackSizes); + state.style()->setNamedGridRowLines(namedGridLines); return; } - // Use the generated StyleBuilder. - StyleBuilder::applyProperty(id, *this, *valueToApply, isInitial, isInherit); -} + case CSSPropertyWebkitGridColumnStart: { + HANDLE_INHERIT_AND_INITIAL(gridItemColumnStart, GridItemColumnStart); + GridPosition columnStartPosition; + if (!createGridPosition(value, columnStartPosition)) + return; + state.style()->setGridItemColumnStart(columnStartPosition); + return; + } + case CSSPropertyWebkitGridColumnEnd: { + HANDLE_INHERIT_AND_INITIAL(gridItemColumnEnd, GridItemColumnEnd); + GridPosition columnEndPosition; + if (!createGridPosition(value, columnEndPosition)) + return; + state.style()->setGridItemColumnEnd(columnEndPosition); + return; + } -RefPtr<CSSValue> StyleResolver::resolvedVariableValue(CSSPropertyID propID, const CSSVariableDependentValue& value) -{ - CSSParser parser(m_state.document()); - return parser.parseVariableDependentValue(propID, value, m_state.style()->customProperties()); + case CSSPropertyWebkitGridRowStart: { + HANDLE_INHERIT_AND_INITIAL(gridItemRowStart, GridItemRowStart); + GridPosition rowStartPosition; + if (!createGridPosition(value, rowStartPosition)) + return; + state.style()->setGridItemRowStart(rowStartPosition); + return; + } + case CSSPropertyWebkitGridRowEnd: { + HANDLE_INHERIT_AND_INITIAL(gridItemRowEnd, GridItemRowEnd); + GridPosition rowEndPosition; + if (!createGridPosition(value, rowEndPosition)) + return; + state.style()->setGridItemRowEnd(rowEndPosition); + return; + } + case CSSPropertyWebkitGridTemplate: { + if (isInherit) { + state.style()->setNamedGridArea(state.parentStyle()->namedGridArea()); + state.style()->setNamedGridAreaRowCount(state.parentStyle()->namedGridAreaRowCount()); + state.style()->setNamedGridAreaColumnCount(state.parentStyle()->namedGridAreaColumnCount()); + return; + } + if (isInitial) { + state.style()->setNamedGridArea(RenderStyle::initialNamedGridArea()); + state.style()->setNamedGridAreaRowCount(RenderStyle::initialNamedGridAreaCount()); + state.style()->setNamedGridAreaColumnCount(RenderStyle::initialNamedGridAreaCount()); + return; + } + + if (value->isPrimitiveValue()) { + ASSERT(toCSSPrimitiveValue(value)->getValueID() == CSSValueNone); + return; + } + + CSSGridTemplateValue* gridTemplateValue = toCSSGridTemplateValue(value); + state.style()->setNamedGridArea(gridTemplateValue->gridAreaMap()); + state.style()->setNamedGridAreaRowCount(gridTemplateValue->rowCount()); + state.style()->setNamedGridAreaColumnCount(gridTemplateValue->columnCount()); + return; + } + + // These properties are aliased and DeprecatedStyleBuilder already applied the property on the prefixed version. + case CSSPropertyTransitionDelay: + case CSSPropertyTransitionDuration: + case CSSPropertyTransitionProperty: + case CSSPropertyTransitionTimingFunction: + return; + // These properties are implemented in the DeprecatedStyleBuilder lookup table. + case CSSPropertyBackgroundAttachment: + case CSSPropertyBackgroundClip: + case CSSPropertyBackgroundColor: + case CSSPropertyBackgroundImage: + case CSSPropertyBackgroundOrigin: + case CSSPropertyBackgroundPositionX: + case CSSPropertyBackgroundPositionY: + case CSSPropertyBackgroundRepeatX: + case CSSPropertyBackgroundRepeatY: + case CSSPropertyBackgroundSize: + case CSSPropertyBorderBottomColor: + case CSSPropertyBorderBottomLeftRadius: + case CSSPropertyBorderBottomRightRadius: + case CSSPropertyBorderBottomStyle: + case CSSPropertyBorderBottomWidth: + case CSSPropertyBorderCollapse: + case CSSPropertyBorderImageOutset: + case CSSPropertyBorderImageRepeat: + case CSSPropertyBorderImageSlice: + case CSSPropertyBorderImageSource: + case CSSPropertyBorderImageWidth: + case CSSPropertyBorderLeftColor: + case CSSPropertyBorderLeftStyle: + case CSSPropertyBorderLeftWidth: + case CSSPropertyBorderRightColor: + case CSSPropertyBorderRightStyle: + case CSSPropertyBorderRightWidth: + case CSSPropertyBorderTopColor: + case CSSPropertyBorderTopLeftRadius: + case CSSPropertyBorderTopRightRadius: + case CSSPropertyBorderTopStyle: + case CSSPropertyBorderTopWidth: + case CSSPropertyBottom: + case CSSPropertyBoxSizing: + case CSSPropertyCaptionSide: + case CSSPropertyClear: + case CSSPropertyClip: + case CSSPropertyColor: + case CSSPropertyCounterIncrement: + case CSSPropertyCounterReset: + case CSSPropertyCursor: + case CSSPropertyDirection: + case CSSPropertyDisplay: + case CSSPropertyEmptyCells: + case CSSPropertyFloat: + case CSSPropertyFontSize: + case CSSPropertyFontStyle: + case CSSPropertyFontVariant: + case CSSPropertyFontWeight: + case CSSPropertyHeight: +#if ENABLE(CSS_IMAGE_ORIENTATION) + case CSSPropertyImageOrientation: +#endif + case CSSPropertyImageRendering: +#if ENABLE(CSS_IMAGE_RESOLUTION) + case CSSPropertyImageResolution: +#endif + case CSSPropertyLeft: + case CSSPropertyLetterSpacing: + case CSSPropertyLineHeight: + case CSSPropertyListStyleImage: + case CSSPropertyListStylePosition: + case CSSPropertyListStyleType: + case CSSPropertyMarginBottom: + case CSSPropertyMarginLeft: + case CSSPropertyMarginRight: + case CSSPropertyMarginTop: + case CSSPropertyMaxHeight: + case CSSPropertyMaxWidth: + case CSSPropertyMinHeight: + case CSSPropertyMinWidth: + case CSSPropertyObjectFit: + case CSSPropertyOpacity: + case CSSPropertyOrphans: + case CSSPropertyOutlineColor: + case CSSPropertyOutlineOffset: + case CSSPropertyOutlineStyle: + case CSSPropertyOutlineWidth: + case CSSPropertyOverflowWrap: + case CSSPropertyOverflowX: + case CSSPropertyOverflowY: + case CSSPropertyPaddingBottom: + case CSSPropertyPaddingLeft: + case CSSPropertyPaddingRight: + case CSSPropertyPaddingTop: + case CSSPropertyPageBreakAfter: + case CSSPropertyPageBreakBefore: + case CSSPropertyPageBreakInside: + case CSSPropertyPointerEvents: + case CSSPropertyPosition: + case CSSPropertyResize: + case CSSPropertyRight: + case CSSPropertySize: + case CSSPropertySpeak: + case CSSPropertyTabSize: + case CSSPropertyTableLayout: + case CSSPropertyTextAlign: + case CSSPropertyTextDecoration: + case CSSPropertyTextIndent: + case CSSPropertyTextOverflow: + case CSSPropertyTextRendering: + case CSSPropertyTextTransform: + case CSSPropertyTop: + case CSSPropertyUnicodeBidi: + case CSSPropertyVerticalAlign: + case CSSPropertyVisibility: + case CSSPropertyWebkitAnimationDelay: + case CSSPropertyWebkitAnimationDirection: + case CSSPropertyWebkitAnimationDuration: + case CSSPropertyWebkitAnimationFillMode: + case CSSPropertyWebkitAnimationIterationCount: + case CSSPropertyWebkitAnimationName: + case CSSPropertyWebkitAnimationPlayState: + case CSSPropertyWebkitAnimationTimingFunction: + case CSSPropertyWebkitAppearance: + case CSSPropertyWebkitAspectRatio: + case CSSPropertyWebkitBackfaceVisibility: + case CSSPropertyWebkitBackgroundClip: + case CSSPropertyWebkitBackgroundComposite: + case CSSPropertyWebkitBackgroundOrigin: + case CSSPropertyWebkitBackgroundSize: + case CSSPropertyWebkitBorderFit: + case CSSPropertyWebkitBorderHorizontalSpacing: + case CSSPropertyWebkitBorderImage: + case CSSPropertyWebkitBorderVerticalSpacing: + case CSSPropertyWebkitBoxAlign: +#if ENABLE(CSS_BOX_DECORATION_BREAK) + case CSSPropertyWebkitBoxDecorationBreak: +#endif + case CSSPropertyWebkitBoxDirection: + case CSSPropertyWebkitBoxFlex: + case CSSPropertyWebkitBoxFlexGroup: + case CSSPropertyWebkitBoxLines: + case CSSPropertyWebkitBoxOrdinalGroup: + case CSSPropertyWebkitBoxOrient: + case CSSPropertyWebkitBoxPack: + case CSSPropertyWebkitColorCorrection: + case CSSPropertyWebkitColumnAxis: + case CSSPropertyWebkitColumnBreakAfter: + case CSSPropertyWebkitColumnBreakBefore: + case CSSPropertyWebkitColumnBreakInside: + case CSSPropertyWebkitColumnCount: + case CSSPropertyWebkitColumnGap: + case CSSPropertyWebkitColumnProgression: + case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyWebkitColumnRuleStyle: + case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyWebkitColumnSpan: + case CSSPropertyWebkitColumnWidth: +#if ENABLE(CURSOR_VISIBILITY) + case CSSPropertyWebkitCursorVisibility: +#endif + case CSSPropertyWebkitAlignContent: + case CSSPropertyWebkitAlignItems: + case CSSPropertyWebkitAlignSelf: + case CSSPropertyWebkitFlexBasis: + case CSSPropertyWebkitFlexDirection: + case CSSPropertyWebkitFlexGrow: + case CSSPropertyWebkitFlexShrink: + case CSSPropertyWebkitFlexWrap: + case CSSPropertyWebkitJustifyContent: + case CSSPropertyWebkitOrder: +#if ENABLE(CSS_REGIONS) + case CSSPropertyWebkitFlowFrom: + case CSSPropertyWebkitFlowInto: +#endif + case CSSPropertyWebkitFontKerning: + case CSSPropertyWebkitFontSmoothing: + case CSSPropertyWebkitFontVariantLigatures: + case CSSPropertyWebkitHighlight: + case CSSPropertyWebkitHyphenateCharacter: + case CSSPropertyWebkitHyphenateLimitAfter: + case CSSPropertyWebkitHyphenateLimitBefore: + case CSSPropertyWebkitHyphenateLimitLines: + case CSSPropertyWebkitHyphens: + case CSSPropertyWebkitLineAlign: + case CSSPropertyWebkitLineBreak: + case CSSPropertyWebkitLineClamp: + case CSSPropertyWebkitLineGrid: + case CSSPropertyWebkitLineSnap: + case CSSPropertyWebkitMarqueeDirection: + case CSSPropertyWebkitMarqueeIncrement: + case CSSPropertyWebkitMarqueeRepetition: + case CSSPropertyWebkitMarqueeSpeed: + case CSSPropertyWebkitMarqueeStyle: + case CSSPropertyWebkitMaskBoxImage: + case CSSPropertyWebkitMaskBoxImageOutset: + case CSSPropertyWebkitMaskBoxImageRepeat: + case CSSPropertyWebkitMaskBoxImageSlice: + case CSSPropertyWebkitMaskBoxImageSource: + case CSSPropertyWebkitMaskBoxImageWidth: + case CSSPropertyWebkitMaskClip: + case CSSPropertyWebkitMaskComposite: + case CSSPropertyWebkitMaskImage: + case CSSPropertyWebkitMaskOrigin: + case CSSPropertyWebkitMaskPositionX: + case CSSPropertyWebkitMaskPositionY: + case CSSPropertyWebkitMaskRepeatX: + case CSSPropertyWebkitMaskRepeatY: + case CSSPropertyWebkitMaskSize: + case CSSPropertyWebkitMaskSourceType: + case CSSPropertyWebkitNbspMode: + case CSSPropertyWebkitPerspectiveOrigin: + case CSSPropertyWebkitPerspectiveOriginX: + case CSSPropertyWebkitPerspectiveOriginY: + case CSSPropertyWebkitPrintColorAdjust: +#if ENABLE(CSS_REGIONS) + case CSSPropertyWebkitRegionBreakAfter: + case CSSPropertyWebkitRegionBreakBefore: + case CSSPropertyWebkitRegionBreakInside: + case CSSPropertyWebkitRegionFragment: +#endif + case CSSPropertyWebkitRtlOrdering: + case CSSPropertyWebkitRubyPosition: + case CSSPropertyWebkitTextCombine: +#if ENABLE(CSS3_TEXT) + case CSSPropertyWebkitTextAlignLast: + case CSSPropertyWebkitTextJustify: +#endif // CSS3_TEXT + case CSSPropertyWebkitTextDecorationLine: + case CSSPropertyWebkitTextDecorationStyle: + case CSSPropertyWebkitTextDecorationColor: + case CSSPropertyWebkitTextDecorationSkip: + case CSSPropertyWebkitTextUnderlinePosition: + case CSSPropertyWebkitTextEmphasisColor: + case CSSPropertyWebkitTextEmphasisPosition: + case CSSPropertyWebkitTextEmphasisStyle: + case CSSPropertyWebkitTextFillColor: + case CSSPropertyWebkitTextSecurity: + case CSSPropertyWebkitTextStrokeColor: + case CSSPropertyWebkitTransformOriginX: + case CSSPropertyWebkitTransformOriginY: + case CSSPropertyWebkitTransformOriginZ: + case CSSPropertyWebkitTransformStyle: + case CSSPropertyWebkitTransitionDelay: + case CSSPropertyWebkitTransitionDuration: + case CSSPropertyWebkitTransitionProperty: + case CSSPropertyWebkitTransitionTimingFunction: + case CSSPropertyWebkitUserDrag: + case CSSPropertyWebkitUserModify: + case CSSPropertyWebkitUserSelect: + case CSSPropertyWebkitClipPath: +#if ENABLE(CSS_SHAPES) + case CSSPropertyWebkitShapeMargin: + case CSSPropertyWebkitShapePadding: + case CSSPropertyWebkitShapeImageThreshold: + case CSSPropertyWebkitShapeInside: + case CSSPropertyWebkitShapeOutside: +#endif +#if ENABLE(CSS_EXCLUSIONS) + case CSSPropertyWebkitWrapFlow: + case CSSPropertyWebkitWrapThrough: +#endif + case CSSPropertyWhiteSpace: + case CSSPropertyWidows: + case CSSPropertyWidth: + case CSSPropertyWordBreak: + case CSSPropertyWordSpacing: + case CSSPropertyWordWrap: + case CSSPropertyZIndex: + case CSSPropertyZoom: +#if ENABLE(CSS_DEVICE_ADAPTATION) + case CSSPropertyMaxZoom: + case CSSPropertyMinZoom: + case CSSPropertyOrientation: + case CSSPropertyUserZoom: +#endif + ASSERT_NOT_REACHED(); + return; + default: +#if ENABLE(SVG) + // Try the SVG properties + applySVGProperty(id, value); +#endif + return; + } } -PassRefPtr<StyleImage> StyleResolver::styleImage(CSSPropertyID property, CSSValue& value) +PassRefPtr<StyleImage> StyleResolver::styleImage(CSSPropertyID property, CSSValue* value) { - if (is<CSSImageValue>(value)) - return cachedOrPendingFromValue(property, downcast<CSSImageValue>(value)); + if (value->isImageValue()) + return cachedOrPendingFromValue(property, toCSSImageValue(value)); - if (is<CSSImageGeneratorValue>(value)) { - if (is<CSSGradientValue>(value)) - return generatedOrPendingFromValue(property, *downcast<CSSGradientValue>(value).gradientWithStylesResolved(this)); - return generatedOrPendingFromValue(property, downcast<CSSImageGeneratorValue>(value)); + if (value->isImageGeneratorValue()) { + if (value->isGradientValue()) + return generatedOrPendingFromValue(property, *toCSSGradientValue(value)->gradientWithStylesResolved(this)); + return generatedOrPendingFromValue(property, toCSSImageGeneratorValue(*value)); } #if ENABLE(CSS_IMAGE_SET) - if (is<CSSImageSetValue>(value)) - return setOrPendingFromValue(property, downcast<CSSImageSetValue>(value)); + if (value->isImageSetValue()) + return setOrPendingFromValue(property, toCSSImageSetValue(value)); #endif - if (is<CSSCursorImageValue>(value)) - return cursorOrPendingFromValue(property, downcast<CSSCursorImageValue>(value)); + if (value->isCursorImageValue()) + return cursorOrPendingFromValue(property, toCSSCursorImageValue(value)); - return nullptr; + return 0; } -PassRefPtr<StyleImage> StyleResolver::cachedOrPendingFromValue(CSSPropertyID property, CSSImageValue& value) +PassRefPtr<StyleImage> StyleResolver::cachedOrPendingFromValue(CSSPropertyID property, CSSImageValue* value) { - RefPtr<StyleImage> image = value.cachedOrPendingImage(); + RefPtr<StyleImage> image = value->cachedOrPendingImage(); if (image && image->isPendingImage()) - m_state.pendingImageProperties().set(property, &value); + m_state.pendingImageProperties().set(property, value); return image.release(); } PassRefPtr<StyleImage> StyleResolver::generatedOrPendingFromValue(CSSPropertyID property, CSSImageGeneratorValue& value) { - if (is<CSSFilterImageValue>(value)) { +#if ENABLE(CSS_FILTERS) + if (value.isFilterImageValue()) { // FilterImage needs to calculate FilterOperations. - downcast<CSSFilterImageValue>(value).createFilterOperations(this); + toCSSFilterImageValue(value).createFilterOperations(this); } - +#endif if (value.isPending()) { m_state.pendingImageProperties().set(property, &value); return StylePendingImage::create(&value); @@ -1663,20 +3144,20 @@ PassRefPtr<StyleImage> StyleResolver::generatedOrPendingFromValue(CSSPropertyID } #if ENABLE(CSS_IMAGE_SET) -PassRefPtr<StyleImage> StyleResolver::setOrPendingFromValue(CSSPropertyID property, CSSImageSetValue& value) +PassRefPtr<StyleImage> StyleResolver::setOrPendingFromValue(CSSPropertyID property, CSSImageSetValue* value) { - RefPtr<StyleImage> image = value.cachedOrPendingImageSet(document()); + RefPtr<StyleImage> image = value->cachedOrPendingImageSet(document()); if (image && image->isPendingImage()) - m_state.pendingImageProperties().set(property, &value); + m_state.pendingImageProperties().set(property, value); return image.release(); } #endif -PassRefPtr<StyleImage> StyleResolver::cursorOrPendingFromValue(CSSPropertyID property, CSSCursorImageValue& value) +PassRefPtr<StyleImage> StyleResolver::cursorOrPendingFromValue(CSSPropertyID property, CSSCursorImageValue* value) { - RefPtr<StyleImage> image = value.cachedOrPendingImage(document()); + RefPtr<StyleImage> image = value->cachedOrPendingImage(document()); if (image && image->isPendingImage()) - m_state.pendingImageProperties().set(property, &value); + m_state.pendingImageProperties().set(property, value); return image.release(); } @@ -1686,7 +3167,7 @@ void StyleResolver::checkForTextSizeAdjust(RenderStyle* style) if (style->textSizeAdjust().isAuto()) return; - auto newFontDescription = style->fontDescription(); + FontDescription newFontDescription(style->fontDescription()); if (!style->textSizeAdjust().isNone()) newFontDescription.setComputedSize(newFontDescription.specifiedSize() * style->textSizeAdjust().multiplier()); else @@ -1697,35 +3178,38 @@ void StyleResolver::checkForTextSizeAdjust(RenderStyle* style) void StyleResolver::checkForZoomChange(RenderStyle* style, RenderStyle* parentStyle) { - if (!parentStyle) - return; - - if (style->effectiveZoom() == parentStyle->effectiveZoom() && style->textZoom() == parentStyle->textZoom()) + if (style->effectiveZoom() == parentStyle->effectiveZoom()) return; - const auto& childFont = style->fontDescription(); - auto newFontDescription = childFont; + const FontDescription& childFont = style->fontDescription(); + FontDescription newFontDescription(childFont); setFontSize(newFontDescription, childFont.specifiedSize()); style->setFontDescription(newFontDescription); } void StyleResolver::checkForGenericFamilyChange(RenderStyle* style, RenderStyle* parentStyle) { - const auto& childFont = style->fontDescription(); + const FontDescription& childFont = style->fontDescription(); if (childFont.isAbsoluteSize() || !parentStyle) return; - const auto& parentFont = parentStyle->fontDescription(); + const FontDescription& parentFont = parentStyle->fontDescription(); if (childFont.useFixedDefaultSize() == parentFont.useFixedDefaultSize()) return; + + // For now, lump all families but monospace together. + if (childFont.genericFamily() != FontDescription::MonospaceFamily + && parentFont.genericFamily() != FontDescription::MonospaceFamily) + return; + // We know the parent is monospace or the child is monospace, and that font // size was unspecified. We want to scale our font size as appropriate. // If the font uses a keyword size, then we refetch from the table rather than // multiplying by our scale factor. float size; - if (CSSValueID sizeIdentifier = childFont.keywordSizeAsIdentifier()) - size = Style::fontSizeForKeyword(sizeIdentifier, childFont.useFixedDefaultSize(), document()); + if (childFont.keywordSize()) + size = Style::fontSizeForKeyword(CSSValueXxSmall + childFont.keywordSize() - 1, childFont.useFixedDefaultSize(), document()); else { Settings* settings = documentSettings(); float fixedScaleFactor = (settings && settings->defaultFixedFontSize() && settings->defaultFontSize()) @@ -1736,23 +3220,28 @@ void StyleResolver::checkForGenericFamilyChange(RenderStyle* style, RenderStyle* childFont.specifiedSize() * fixedScaleFactor; } - auto newFontDescription = childFont; + FontDescription newFontDescription(childFont); setFontSize(newFontDescription, size); style->setFontDescription(newFontDescription); } void StyleResolver::initializeFontStyle(Settings* settings) { - FontCascadeDescription fontDescription; - if (settings) - fontDescription.setRenderingMode(settings->fontRenderingMode()); - fontDescription.setOneFamily(standardFamily); - fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium); + FontDescription fontDescription; + fontDescription.setGenericFamily(FontDescription::StandardFamily); + fontDescription.setRenderingMode(settings->fontRenderingMode()); + fontDescription.setUsePrinterFont(document().printing() || !settings->screenFontSubstitutionEnabled()); + const AtomicString& standardFontFamily = documentSettings()->standardFontFamily(); + if (!standardFontFamily.isEmpty()) + fontDescription.setOneFamily(standardFontFamily); + fontDescription.setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1); setFontSize(fontDescription, Style::fontSizeForKeyword(CSSValueMedium, false, document())); + m_state.style()->setLineHeight(RenderStyle::initialLineHeight()); + m_state.setLineHeightValue(0); setFontDescription(fontDescription); } -void StyleResolver::setFontSize(FontCascadeDescription& fontDescription, float size) +void StyleResolver::setFontSize(FontDescription& fontDescription, float size) { fontDescription.setSpecifiedSize(size); fontDescription.setComputedSize(Style::computedFontSizeFromSpecifiedSize(size, fontDescription.isAbsoluteSize(), useSVGZoomRules(), m_state.style(), document())); @@ -1795,9 +3284,9 @@ static Color colorForCSSValue(CSSValueID cssValueId) return RenderTheme::defaultTheme()->systemColor(cssValueId); } -bool StyleResolver::colorFromPrimitiveValueIsDerivedFromElement(const CSSPrimitiveValue& value) +bool StyleResolver::colorFromPrimitiveValueIsDerivedFromElement(CSSPrimitiveValue* value) { - int ident = value.getValueID(); + int ident = value->getValueID(); switch (ident) { case CSSValueWebkitText: case CSSValueWebkitLink: @@ -1809,13 +3298,13 @@ bool StyleResolver::colorFromPrimitiveValueIsDerivedFromElement(const CSSPrimiti } } -Color StyleResolver::colorFromPrimitiveValue(const CSSPrimitiveValue& value, bool forVisitedLink) const +Color StyleResolver::colorFromPrimitiveValue(CSSPrimitiveValue* value, bool forVisitedLink) const { - if (value.isRGBColor()) - return Color(value.getRGBA32Value()); + if (value->isRGBColor()) + return Color(value->getRGBA32Value()); const State& state = m_state; - CSSValueID ident = value.getValueID(); + CSSValueID ident = value->getValueID(); switch (ident) { case 0: return Color(); @@ -1839,7 +3328,7 @@ void StyleResolver::addViewportDependentMediaQueryResult(const MediaQueryExp* ex m_viewportDependentMediaQueryResults.append(std::make_unique<MediaQueryResult>(*expr, result)); } -bool StyleResolver::hasMediaQueriesAffectedByViewportChange() const +bool StyleResolver::affectedByViewportChange() const { unsigned s = m_viewportDependentMediaQueryResults.size(); for (unsigned i = 0; i < s; i++) { @@ -1849,6 +3338,7 @@ bool StyleResolver::hasMediaQueriesAffectedByViewportChange() const return false; } +#if ENABLE(CSS_FILTERS) static FilterOperation::OperationType filterOperationForType(WebKitCSSFilterValue::FilterOperationType type) { switch (type) { @@ -1880,6 +3370,7 @@ static FilterOperation::OperationType filterOperationForType(WebKitCSSFilterValu return FilterOperation::NONE; } +#if ENABLE(CSS_FILTERS) && ENABLE(SVG) void StyleResolver::loadPendingSVGDocuments() { State& state = m_state; @@ -1891,82 +3382,88 @@ void StyleResolver::loadPendingSVGDocuments() if (!state.style() || !state.style()->hasFilter() || state.filtersWithPendingSVGDocuments().isEmpty()) return; - ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); - options.setContentSecurityPolicyImposition(m_state.element() && m_state.element()->isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck); - - CachedResourceLoader& cachedResourceLoader = state.document().cachedResourceLoader(); - - for (auto& filterOperation : state.filtersWithPendingSVGDocuments()) - filterOperation->getOrCreateCachedSVGDocumentReference()->load(cachedResourceLoader, options); + CachedResourceLoader* cachedResourceLoader = state.document().cachedResourceLoader(); + for (auto filterOperation : state.filtersWithPendingSVGDocuments()) + filterOperation->getOrCreateCachedSVGDocumentReference()->load(cachedResourceLoader); state.filtersWithPendingSVGDocuments().clear(); } +#endif -bool StyleResolver::createFilterOperations(const CSSValue& inValue, FilterOperations& outOperations) +bool StyleResolver::createFilterOperations(CSSValue* inValue, FilterOperations& outOperations) { State& state = m_state; + RenderStyle* style = state.style(); + RenderStyle* rootStyle = state.rootElementStyle(); ASSERT(outOperations.isEmpty()); - if (is<CSSPrimitiveValue>(inValue)) { - auto& primitiveValue = downcast<CSSPrimitiveValue>(inValue); - if (primitiveValue.getValueID() == CSSValueNone) + if (!inValue) + return false; + + if (inValue->isPrimitiveValue()) { + CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(inValue); + if (primitiveValue->getValueID() == CSSValueNone) return true; } - if (!is<CSSValueList>(inValue)) + if (!inValue->isValueList()) return false; + float zoomFactor = style ? style->effectiveZoom() : 1; FilterOperations operations; - for (auto& currentValue : downcast<CSSValueList>(inValue)) { - if (!is<WebKitCSSFilterValue>(currentValue.get())) + for (CSSValueListIterator i = inValue; i.hasMore(); i.advance()) { + CSSValue* currValue = i.value(); + if (!currValue->isWebKitCSSFilterValue()) continue; - auto& filterValue = downcast<WebKitCSSFilterValue>(currentValue.get()); - FilterOperation::OperationType operationType = filterOperationForType(filterValue.operationType()); + WebKitCSSFilterValue* filterValue = toWebKitCSSFilterValue(i.value()); + FilterOperation::OperationType operationType = filterOperationForType(filterValue->operationType()); if (operationType == FilterOperation::REFERENCE) { - if (filterValue.length() != 1) +#if ENABLE(SVG) + if (filterValue->length() != 1) continue; - auto& argument = *filterValue.itemWithoutBoundsCheck(0); + CSSValue* argument = filterValue->itemWithoutBoundsCheck(0); - if (!is<CSSPrimitiveValue>(argument)) + if (!argument->isPrimitiveValue()) continue; - auto& primitiveValue = downcast<CSSPrimitiveValue>(argument); + CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(*argument); String cssUrl = primitiveValue.getStringValue(); URL url = m_state.document().completeURL(cssUrl); - RefPtr<ReferenceFilterOperation> operation = ReferenceFilterOperation::create(cssUrl, url.fragmentIdentifier()); + RefPtr<ReferenceFilterOperation> operation = ReferenceFilterOperation::create(cssUrl, url.fragmentIdentifier(), operationType); if (SVGURIReference::isExternalURIReference(cssUrl, m_state.document())) state.filtersWithPendingSVGDocuments().append(operation); operations.operations().append(operation); +#endif continue; } // Check that all parameters are primitive values, with the // exception of drop shadow which has a CSSShadowValue parameter. - const CSSPrimitiveValue* firstValue = nullptr; + CSSPrimitiveValue* firstValue = nullptr; if (operationType != FilterOperation::DROP_SHADOW) { bool haveNonPrimitiveValue = false; - for (unsigned j = 0; j < filterValue.length(); ++j) { - if (!is<CSSPrimitiveValue>(*filterValue.itemWithoutBoundsCheck(j))) { + for (unsigned j = 0; j < filterValue->length(); ++j) { + if (!filterValue->itemWithoutBoundsCheck(j)->isPrimitiveValue()) { haveNonPrimitiveValue = true; break; } } if (haveNonPrimitiveValue) continue; - if (filterValue.length()) - firstValue = downcast<CSSPrimitiveValue>(filterValue.itemWithoutBoundsCheck(0)); + if (filterValue->length()) + firstValue = toCSSPrimitiveValue(filterValue->itemWithoutBoundsCheck(0)); } - switch (filterValue.operationType()) { + switch (filterValue->operationType()) { case WebKitCSSFilterValue::GrayscaleFilterOperation: case WebKitCSSFilterValue::SepiaFilterOperation: case WebKitCSSFilterValue::SaturateFilterOperation: { double amount = 1; - if (filterValue.length() == 1) { + if (filterValue->length() == 1) { amount = firstValue->getDoubleValue(); if (firstValue->isPercentage()) amount /= 100; @@ -1977,7 +3474,7 @@ bool StyleResolver::createFilterOperations(const CSSValue& inValue, FilterOperat } case WebKitCSSFilterValue::HueRotateFilterOperation: { double angle = 0; - if (filterValue.length() == 1) + if (filterValue->length() == 1) angle = firstValue->computeDegrees(); operations.operations().append(BasicColorMatrixFilterOperation::create(angle, operationType)); @@ -1987,8 +3484,8 @@ bool StyleResolver::createFilterOperations(const CSSValue& inValue, FilterOperat case WebKitCSSFilterValue::BrightnessFilterOperation: case WebKitCSSFilterValue::ContrastFilterOperation: case WebKitCSSFilterValue::OpacityFilterOperation: { - double amount = (filterValue.operationType() == WebKitCSSFilterValue::BrightnessFilterOperation) ? 0 : 1; - if (filterValue.length() == 1) { + double amount = (filterValue->operationType() == WebKitCSSFilterValue::BrightnessFilterOperation) ? 0 : 1; + if (filterValue->length() == 1) { amount = firstValue->getDoubleValue(); if (firstValue->isPercentage()) amount /= 100; @@ -1999,32 +3496,38 @@ bool StyleResolver::createFilterOperations(const CSSValue& inValue, FilterOperat } case WebKitCSSFilterValue::BlurFilterOperation: { Length stdDeviation = Length(0, Fixed); - if (filterValue.length() >= 1) - stdDeviation = convertToFloatLength(firstValue, state.cssToLengthConversionData()); + if (filterValue->length() >= 1) + stdDeviation = convertToFloatLength(firstValue, style, rootStyle, zoomFactor); if (stdDeviation.isUndefined()) return false; - operations.operations().append(BlurFilterOperation::create(stdDeviation)); + operations.operations().append(BlurFilterOperation::create(stdDeviation, operationType)); break; } case WebKitCSSFilterValue::DropShadowFilterOperation: { - if (filterValue.length() != 1) + if (filterValue->length() != 1) return false; - auto& cssValue = *filterValue.itemWithoutBoundsCheck(0); - if (!is<CSSShadowValue>(cssValue)) + CSSValue* cssValue = filterValue->itemWithoutBoundsCheck(0); + if (!cssValue->isShadowValue()) continue; - auto& item = downcast<CSSShadowValue>(cssValue); - int x = item.x->computeLength<int>(state.cssToLengthConversionData()); - int y = item.y->computeLength<int>(state.cssToLengthConversionData()); + CSSShadowValue* item = toCSSShadowValue(cssValue); + int x = item->x->computeLength<int>(style, rootStyle, zoomFactor); + if (item->x->isViewportPercentageLength()) + x = viewportPercentageValue(*item->x, x); + int y = item->y->computeLength<int>(style, rootStyle, zoomFactor); + if (item->y->isViewportPercentageLength()) + y = viewportPercentageValue(*item->y, y); IntPoint location(x, y); - int blur = item.blur ? item.blur->computeLength<int>(state.cssToLengthConversionData()) : 0; + int blur = item->blur ? item->blur->computeLength<int>(style, rootStyle, zoomFactor) : 0; + if (item->blur && item->blur->isViewportPercentageLength()) + blur = viewportPercentageValue(*item->blur, blur); Color color; - if (item.color) - color = colorFromPrimitiveValue(*item.color); + if (item->color) + color = colorFromPrimitiveValue(item->color.get()); - operations.operations().append(DropShadowFilterOperation::create(location, blur, color.isValid() ? color : Color::transparent)); + operations.operations().append(DropShadowFilterOperation::create(location, blur, color.isValid() ? color : Color::transparent, operationType)); break; } case WebKitCSSFilterValue::UnknownFilterOperation: @@ -2038,32 +3541,32 @@ bool StyleResolver::createFilterOperations(const CSSValue& inValue, FilterOperat return true; } -PassRefPtr<StyleImage> StyleResolver::loadPendingImage(const StylePendingImage& pendingImage, const ResourceLoaderOptions& options) +#endif + +PassRefPtr<StyleImage> StyleResolver::loadPendingImage(StylePendingImage* pendingImage, const ResourceLoaderOptions& options) { - if (auto imageValue = pendingImage.cssImageValue()) + if (auto imageValue = pendingImage->cssImageValue()) return imageValue->cachedImage(m_state.document().cachedResourceLoader(), options); - if (auto imageGeneratorValue = pendingImage.cssImageGeneratorValue()) { - imageGeneratorValue->loadSubimages(m_state.document().cachedResourceLoader(), options); + if (auto imageGeneratorValue = pendingImage->cssImageGeneratorValue()) { + imageGeneratorValue->loadSubimages(m_state.document().cachedResourceLoader()); return StyleGeneratedImage::create(*imageGeneratorValue); } - if (auto cursorImageValue = pendingImage.cssCursorImageValue()) - return cursorImageValue->cachedImage(m_state.document().cachedResourceLoader(), options); + if (auto cursorImageValue = pendingImage->cssCursorImageValue()) + return cursorImageValue->cachedImage(m_state.document().cachedResourceLoader()); #if ENABLE(CSS_IMAGE_SET) - if (auto imageSetValue = pendingImage.cssImageSetValue()) + if (CSSImageSetValue* imageSetValue = pendingImage->cssImageSetValue()) return imageSetValue->cachedImageSet(m_state.document().cachedResourceLoader(), options); #endif return nullptr; } -PassRefPtr<StyleImage> StyleResolver::loadPendingImage(const StylePendingImage& pendingImage) +PassRefPtr<StyleImage> StyleResolver::loadPendingImage(StylePendingImage* pendingImage) { - ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); - options.setContentSecurityPolicyImposition(m_state.element() && m_state.element()->isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck); - return loadPendingImage(pendingImage, options); + return loadPendingImage(pendingImage, CachedResourceLoader::defaultCachedResourceOptions()); } #if ENABLE(CSS_SHAPES) @@ -2073,15 +3576,14 @@ void StyleResolver::loadPendingShapeImage(ShapeValue* shapeValue) return; StyleImage* image = shapeValue->image(); - if (!is<StylePendingImage>(image)) + if (!image || !image->isPendingImage()) return; - auto& pendingImage = downcast<StylePendingImage>(*image); + StylePendingImage* pendingImage = static_cast<StylePendingImage*>(image); ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); - options.setRequestOriginPolicy(PotentiallyCrossOriginEnabled); - options.setAllowCredentials(DoNotAllowStoredCredentials); - options.setContentSecurityPolicyImposition(m_state.element() && m_state.element()->isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck); + options.requestOriginPolicy = PotentiallyCrossOriginEnabled; + options.allowCredentials = DoNotAllowStoredCredentials; shapeValue->setImage(loadPendingImage(pendingImage, options)); } @@ -2089,9 +3591,6 @@ void StyleResolver::loadPendingShapeImage(ShapeValue* shapeValue) void StyleResolver::loadPendingImages() { - RELEASE_ASSERT(!m_inLoadPendingImages); - TemporaryChange<bool> changeInLoadPendingImages(m_inLoadPendingImages, true); - if (m_state.pendingImageProperties().isEmpty()) return; @@ -2101,20 +3600,20 @@ void StyleResolver::loadPendingImages() switch (currentProperty) { case CSSPropertyBackgroundImage: { - for (FillLayer* backgroundLayer = &m_state.style()->ensureBackgroundLayers(); backgroundLayer; backgroundLayer = backgroundLayer->next()) { - auto* styleImage = backgroundLayer->image(); - if (is<StylePendingImage>(styleImage)) - backgroundLayer->setImage(loadPendingImage(downcast<StylePendingImage>(*styleImage))); + for (FillLayer* backgroundLayer = m_state.style()->accessBackgroundLayers(); backgroundLayer; backgroundLayer = backgroundLayer->next()) { + if (backgroundLayer->image() && backgroundLayer->image()->isPendingImage()) + backgroundLayer->setImage(loadPendingImage(static_cast<StylePendingImage*>(backgroundLayer->image()))); } break; } case CSSPropertyContent: { for (ContentData* contentData = const_cast<ContentData*>(m_state.style()->contentData()); contentData; contentData = contentData->next()) { - if (is<ImageContentData>(*contentData)) { - auto& styleImage = downcast<ImageContentData>(*contentData).image(); - if (is<StylePendingImage>(styleImage)) { - if (RefPtr<StyleImage> loadedImage = loadPendingImage(downcast<StylePendingImage>(styleImage))) - downcast<ImageContentData>(*contentData).setImage(loadedImage.release()); + if (contentData->isImage()) { + StyleImage* image = static_cast<ImageContentData*>(contentData)->image(); + if (image->isPendingImage()) { + RefPtr<StyleImage> loadedImage = loadPendingImage(static_cast<StylePendingImage*>(image)); + if (loadedImage) + static_cast<ImageContentData*>(contentData)->setImage(loadedImage.release()); } } } @@ -2124,51 +3623,50 @@ void StyleResolver::loadPendingImages() if (CursorList* cursorList = m_state.style()->cursors()) { for (size_t i = 0; i < cursorList->size(); ++i) { CursorData& currentCursor = cursorList->at(i); - auto* styleImage = currentCursor.image(); - if (is<StylePendingImage>(styleImage)) - currentCursor.setImage(loadPendingImage(downcast<StylePendingImage>(*styleImage))); + if (StyleImage* image = currentCursor.image()) { + if (image->isPendingImage()) + currentCursor.setImage(loadPendingImage(static_cast<StylePendingImage*>(image))); + } } } break; } case CSSPropertyListStyleImage: { - auto* styleImage = m_state.style()->listStyleImage(); - if (is<StylePendingImage>(styleImage)) - m_state.style()->setListStyleImage(loadPendingImage(downcast<StylePendingImage>(*styleImage))); + if (m_state.style()->listStyleImage() && m_state.style()->listStyleImage()->isPendingImage()) + m_state.style()->setListStyleImage(loadPendingImage(static_cast<StylePendingImage*>(m_state.style()->listStyleImage()))); break; } case CSSPropertyBorderImageSource: { - auto* styleImage = m_state.style()->borderImageSource(); - if (is<StylePendingImage>(styleImage)) - m_state.style()->setBorderImageSource(loadPendingImage(downcast<StylePendingImage>(*styleImage))); + if (m_state.style()->borderImageSource() && m_state.style()->borderImageSource()->isPendingImage()) + m_state.style()->setBorderImageSource(loadPendingImage(static_cast<StylePendingImage*>(m_state.style()->borderImageSource()))); break; } case CSSPropertyWebkitBoxReflect: { if (StyleReflection* reflection = m_state.style()->boxReflect()) { const NinePieceImage& maskImage = reflection->mask(); - auto* styleImage = maskImage.image(); - if (is<StylePendingImage>(styleImage)) { - RefPtr<StyleImage> loadedImage = loadPendingImage(downcast<StylePendingImage>(*styleImage)); + if (maskImage.image() && maskImage.image()->isPendingImage()) { + RefPtr<StyleImage> loadedImage = loadPendingImage(static_cast<StylePendingImage*>(maskImage.image())); reflection->setMask(NinePieceImage(loadedImage.release(), maskImage.imageSlices(), maskImage.fill(), maskImage.borderSlices(), maskImage.outset(), maskImage.horizontalRule(), maskImage.verticalRule())); } } break; } case CSSPropertyWebkitMaskBoxImageSource: { - auto* styleImage = m_state.style()->maskBoxImageSource(); - if (is<StylePendingImage>(styleImage)) - m_state.style()->setMaskBoxImageSource(loadPendingImage(downcast<StylePendingImage>(*styleImage))); + if (m_state.style()->maskBoxImageSource() && m_state.style()->maskBoxImageSource()->isPendingImage()) + m_state.style()->setMaskBoxImageSource(loadPendingImage(static_cast<StylePendingImage*>(m_state.style()->maskBoxImageSource()))); break; } case CSSPropertyWebkitMaskImage: { - for (FillLayer* maskLayer = &m_state.style()->ensureMaskLayers(); maskLayer; maskLayer = maskLayer->next()) { - auto* styleImage = maskLayer->image(); - if (is<StylePendingImage>(styleImage)) - maskLayer->setImage(loadPendingImage(downcast<StylePendingImage>(*styleImage))); + for (FillLayer* maskLayer = m_state.style()->accessMaskLayers(); maskLayer; maskLayer = maskLayer->next()) { + if (maskLayer->image() && maskLayer->image()->isPendingImage()) + maskLayer->setImage(loadPendingImage(static_cast<StylePendingImage*>(maskLayer->image()))); } break; } #if ENABLE(CSS_SHAPES) + case CSSPropertyWebkitShapeInside: + loadPendingShapeImage(m_state.style()->shapeInside()); + break; case CSSPropertyWebkitShapeOutside: loadPendingShapeImage(m_state.style()->shapeOutside()); break; @@ -2203,8 +3701,10 @@ void StyleResolver::loadPendingResources() // Start loading images referenced by this style. loadPendingImages(); +#if ENABLE(CSS_FILTERS) && ENABLE(SVG) // Start loading the SVG Documents referenced by this style. loadPendingSVGDocuments(); +#endif #ifndef NDEBUG inLoadPendingResources = false; @@ -2212,46 +3712,47 @@ void StyleResolver::loadPendingResources() } inline StyleResolver::MatchedProperties::MatchedProperties() - : possiblyPaddedMember(nullptr) + : possiblyPaddedMember(0) { } -StyleResolver::MatchedProperties::~MatchedProperties() +inline StyleResolver::MatchedProperties::~MatchedProperties() { } -StyleResolver::CascadedProperties::CascadedProperties(TextDirection direction, WritingMode writingMode) - : m_direction(direction) - , m_writingMode(writingMode) +int StyleResolver::viewportPercentageValue(CSSPrimitiveValue& unit, int percentage) { -} + int viewPortHeight = document().renderView()->viewportSize().height() * percentage / 100.0f; + int viewPortWidth = document().renderView()->viewportSize().width() * percentage / 100.0f; -inline bool StyleResolver::CascadedProperties::hasProperty(CSSPropertyID id) const -{ - ASSERT(id < m_propertyIsPresent.size()); - return m_propertyIsPresent[id]; -} + if (unit.isViewportPercentageHeight()) + return viewPortHeight; + if (unit.isViewportPercentageWidth()) + return viewPortWidth; + if (unit.isViewportPercentageMax()) + return std::max(viewPortWidth, viewPortHeight); + if (unit.isViewportPercentageMin()) + return std::min(viewPortWidth, viewPortHeight); -inline StyleResolver::CascadedProperties::Property& StyleResolver::CascadedProperties::property(CSSPropertyID id) -{ - return m_properties[id]; + ASSERT_NOT_REACHED(); + return 0; } -inline bool StyleResolver::CascadedProperties::hasCustomProperty(const String& name) const +StyleResolver::CascadedProperties::CascadedProperties(TextDirection direction, WritingMode writingMode) + : m_direction(direction) + , m_writingMode(writingMode) { - return m_customProperties.contains(name); } -inline StyleResolver::CascadedProperties::Property StyleResolver::CascadedProperties::customProperty(const String& name) const +inline StyleResolver::CascadedProperties::Property& StyleResolver::CascadedProperties::property(CSSPropertyID id) { - return m_customProperties.get(name); + return m_properties[id]; } -void StyleResolver::CascadedProperties::setPropertyInternal(Property& property, CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel) +void StyleResolver::CascadedProperties::setPropertyInternal(Property& property, CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType) { ASSERT(linkMatchType <= SelectorChecker::MatchAll); property.id = id; - property.level = cascadeLevel; if (linkMatchType == SelectorChecker::MatchAll) { property.cssValue[0] = &cssValue; property.cssValue[SelectorChecker::MatchLink] = &cssValue; @@ -2260,7 +3761,7 @@ void StyleResolver::CascadedProperties::setPropertyInternal(Property& property, property.cssValue[linkMatchType] = &cssValue; } -void StyleResolver::CascadedProperties::set(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel) +void StyleResolver::CascadedProperties::set(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType) { if (CSSProperty::isDirectionAwareProperty(id)) id = CSSProperty::resolveDirectionAwareProperty(id, m_direction, m_writingMode); @@ -2268,52 +3769,35 @@ void StyleResolver::CascadedProperties::set(CSSPropertyID id, CSSValue& cssValue ASSERT(!shouldApplyPropertyInParseOrder(id)); auto& property = m_properties[id]; - ASSERT(id < m_propertyIsPresent.size()); - if (id == CSSPropertyCustom) { - m_propertyIsPresent.set(id); - const auto& customValue = downcast<CSSCustomPropertyValue>(cssValue); - bool hasValue = customProperties().contains(customValue.name()); - if (!hasValue) { - Property property; - property.id = id; - memset(property.cssValue, 0, sizeof(property.cssValue)); - setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel); - customProperties().set(customValue.name(), property); - } else { - Property property = customProperties().get(customValue.name()); - setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel); - customProperties().set(customValue.name(), property); - } - return; - } - - if (!m_propertyIsPresent[id]) + if (!m_propertyIsPresent.test(id)) memset(property.cssValue, 0, sizeof(property.cssValue)); m_propertyIsPresent.set(id); - setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel); + setPropertyInternal(property, id, cssValue, linkMatchType); } -void StyleResolver::CascadedProperties::setDeferred(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel) +void StyleResolver::CascadedProperties::setDeferred(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType) { ASSERT(!CSSProperty::isDirectionAwareProperty(id)); ASSERT(shouldApplyPropertyInParseOrder(id)); Property property; memset(property.cssValue, 0, sizeof(property.cssValue)); - setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel); + setPropertyInternal(property, id, cssValue, linkMatchType); m_deferredProperties.append(property); } -void StyleResolver::CascadedProperties::addStyleProperties(const StyleProperties& properties, StyleRule&, bool isImportant, bool inheritedOnly, PropertyWhitelistType propertyWhitelistType, unsigned linkMatchType, CascadeLevel cascadeLevel) +bool StyleResolver::CascadedProperties::addStyleProperties(const StyleProperties& properties, StyleRule&, bool isImportant, bool inheritedOnly, PropertyWhitelistType propertyWhitelistType, unsigned linkMatchType) { for (unsigned i = 0, count = properties.propertyCount(); i < count; ++i) { auto current = properties.propertyAt(i); if (isImportant != current.isImportant()) continue; if (inheritedOnly && !current.isInherited()) { - // We apply the inherited properties only when using the property cache. - // A match with a value that is explicitely inherited should never have been cached. - ASSERT(!current.value()->isInheritedValue()); + // If the property value is explicitly inherited, we need to apply further non-inherited properties + // as they might override the value inherited here. For this reason we don't allow declarations with + // explicitly inherited properties to be cached. + if (current.value()->isInheritedValue()) + return false; continue; } CSSPropertyID propertyID = current.id(); @@ -2326,48 +3810,47 @@ void StyleResolver::CascadedProperties::addStyleProperties(const StyleProperties #endif if (shouldApplyPropertyInParseOrder(propertyID)) - setDeferred(propertyID, *current.value(), linkMatchType, cascadeLevel); + setDeferred(propertyID, *current.value(), linkMatchType); else - set(propertyID, *current.value(), linkMatchType, cascadeLevel); + set(propertyID, *current.value(), linkMatchType); } + return true; } -static CascadeLevel cascadeLevelForIndex(const StyleResolver::MatchResult& matchResult, int index) -{ - if (index >= matchResult.ranges.firstUARule && index <= matchResult.ranges.lastUARule) - return UserAgentLevel; - if (index >= matchResult.ranges.firstUserRule && index <= matchResult.ranges.lastUserRule) - return UserLevel; - return AuthorLevel; -} - -void StyleResolver::CascadedProperties::addMatches(const MatchResult& matchResult, bool important, int startIndex, int endIndex, bool inheritedOnly) +bool StyleResolver::CascadedProperties::addMatches(const MatchResult& matchResult, bool important, int startIndex, int endIndex, bool inheritedOnly) { if (startIndex == -1) - return; + return true; for (int i = startIndex; i <= endIndex; ++i) { - const MatchedProperties& matchedProperties = matchResult.matchedProperties()[i]; - addStyleProperties(*matchedProperties.properties, *matchResult.matchedRules[i], important, inheritedOnly, static_cast<PropertyWhitelistType>(matchedProperties.whitelistType), matchedProperties.linkMatchType, - cascadeLevelForIndex(matchResult, i)); + const MatchedProperties& matchedProperties = matchResult.matchedProperties[i]; + if (!addStyleProperties(*matchedProperties.properties, *matchResult.matchedRules[i], important, inheritedOnly, static_cast<PropertyWhitelistType>(matchedProperties.whitelistType), matchedProperties.linkMatchType)) + return false; } + return true; } -void StyleResolver::CascadedProperties::applyDeferredProperties(StyleResolver& resolver, const MatchResult* matchResult) +void StyleResolver::CascadedProperties::applyDeferredProperties(StyleResolver& resolver) { for (auto& property : m_deferredProperties) - property.apply(resolver, matchResult); + property.apply(resolver); } -void StyleResolver::CascadedProperties::Property::apply(StyleResolver& resolver, const MatchResult* matchResult) +void StyleResolver::CascadedProperties::Property::apply(StyleResolver& resolver) { State& state = resolver.state(); - state.setCascadeLevel(level); - if (cssValue[SelectorChecker::MatchDefault]) { + // FIXME: It would be nice if line-height were less of a special snowflake. + if (id == CSSPropertyLineHeight) { + if (auto value = state.style()->insideLink() == NotInsideLink ? cssValue[0] : cssValue[SelectorChecker::MatchLink]) + state.setLineHeightValue(value); + return; + } + + if (cssValue[0]) { state.setApplyPropertyToRegularStyle(true); state.setApplyPropertyToVisitedLinkStyle(false); - resolver.applyProperty(id, cssValue[SelectorChecker::MatchDefault], SelectorChecker::MatchDefault, matchResult); + resolver.applyProperty(id, cssValue[0]); } if (state.style()->insideLink() == NotInsideLink) @@ -2376,38 +3859,29 @@ void StyleResolver::CascadedProperties::Property::apply(StyleResolver& resolver, if (cssValue[SelectorChecker::MatchLink]) { state.setApplyPropertyToRegularStyle(true); state.setApplyPropertyToVisitedLinkStyle(false); - resolver.applyProperty(id, cssValue[SelectorChecker::MatchLink], SelectorChecker::MatchLink, matchResult); + resolver.applyProperty(id, cssValue[SelectorChecker::MatchLink]); } if (cssValue[SelectorChecker::MatchVisited]) { state.setApplyPropertyToRegularStyle(false); state.setApplyPropertyToVisitedLinkStyle(true); - resolver.applyProperty(id, cssValue[SelectorChecker::MatchVisited], SelectorChecker::MatchVisited, matchResult); + resolver.applyProperty(id, cssValue[SelectorChecker::MatchVisited]); } state.setApplyPropertyToRegularStyle(true); state.setApplyPropertyToVisitedLinkStyle(false); } -void StyleResolver::applyCascadedProperties(CascadedProperties& cascade, int firstProperty, int lastProperty, const MatchResult* matchResult) +void StyleResolver::applyCascadedProperties(CascadedProperties& cascade, int firstProperty, int lastProperty) { for (int id = firstProperty; id <= lastProperty; ++id) { CSSPropertyID propertyID = static_cast<CSSPropertyID>(id); if (!cascade.hasProperty(propertyID)) continue; - if (propertyID == CSSPropertyCustom) { - HashMap<AtomicString, CascadedProperties::Property>::iterator end = cascade.customProperties().end(); - for (HashMap<AtomicString, CascadedProperties::Property>::iterator it = cascade.customProperties().begin(); it != end; ++it) - it->value.apply(*this, matchResult); - continue; - } auto& property = cascade.property(propertyID); ASSERT(!shouldApplyPropertyInParseOrder(propertyID)); - property.apply(*this, matchResult); + property.apply(*this); } - - if (firstProperty == CSSPropertyCustom) - m_state.style()->checkVariablesInCustomProperties(); } } // namespace WebCore |
