diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/style | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/style')
32 files changed, 4409 insertions, 1019 deletions
diff --git a/Source/WebCore/style/AttributeChangeInvalidation.cpp b/Source/WebCore/style/AttributeChangeInvalidation.cpp new file mode 100644 index 000000000..f71c6ea05 --- /dev/null +++ b/Source/WebCore/style/AttributeChangeInvalidation.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AttributeChangeInvalidation.h" + +#include "DocumentRuleSets.h" +#include "ElementIterator.h" +#include "ShadowRoot.h" +#include "StyleInvalidationAnalysis.h" +#include "StyleResolver.h" +#include "StyleScope.h" + +namespace WebCore { +namespace Style { + +static bool mayBeAffectedByAttributeChange(DocumentRuleSets& ruleSets, bool isHTML, const QualifiedName& attributeName) +{ + auto& nameSet = isHTML ? ruleSets.features().attributeCanonicalLocalNamesInRules : ruleSets.features().attributeLocalNamesInRules; + return nameSet.contains(attributeName.localName()); +} + +static bool mayBeAffectedByHostRules(const Element& element, const QualifiedName& attributeName) +{ + auto* shadowRoot = element.shadowRoot(); + if (!shadowRoot) + return false; + auto& shadowRuleSets = shadowRoot->styleScope().resolver().ruleSets(); + if (shadowRuleSets.authorStyle().hostPseudoClassRules().isEmpty()) + return false; + + return mayBeAffectedByAttributeChange(shadowRuleSets, element.isHTMLElement(), attributeName); +} + +static bool mayBeAffectedBySlottedRules(const Element& element, const QualifiedName& attributeName) +{ + for (auto* shadowRoot : assignedShadowRootsIfSlotted(element)) { + auto& ruleSets = shadowRoot->styleScope().resolver().ruleSets(); + if (ruleSets.authorStyle().slottedPseudoElementRules().isEmpty()) + continue; + if (mayBeAffectedByAttributeChange(ruleSets, element.isHTMLElement(), attributeName)) + return true; + } + return false; +} + +void AttributeChangeInvalidation::invalidateStyle(const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) +{ + if (newValue == oldValue) + return; + + auto& ruleSets = m_element.styleResolver().ruleSets(); + bool isHTML = m_element.isHTMLElement(); + + bool mayAffectStyle = mayBeAffectedByAttributeChange(ruleSets, isHTML, attributeName) + || mayBeAffectedByHostRules(m_element, attributeName) + || mayBeAffectedBySlottedRules(m_element, attributeName); + + if (!mayAffectStyle) + return; + + if (!isHTML) { + m_element.invalidateStyleForSubtree(); + return; + } + + if (m_element.shadowRoot() && ruleSets.authorStyle().hasShadowPseudoElementRules()) { + m_element.invalidateStyleForSubtree(); + return; + } + + m_element.invalidateStyle(); + + if (!childrenOfType<Element>(m_element).first()) + return; + + auto* attributeRules = ruleSets.ancestorAttributeRulesForHTML(attributeName.localName()); + if (!attributeRules) + return; + + // Check if descendants may be affected by this attribute change. + for (auto* selector : attributeRules->attributeSelectors) { + bool oldMatches = oldValue.isNull() ? false : SelectorChecker::attributeSelectorMatches(m_element, attributeName, oldValue, *selector); + bool newMatches = newValue.isNull() ? false : SelectorChecker::attributeSelectorMatches(m_element, attributeName, newValue, *selector); + + if (oldMatches != newMatches) { + m_descendantInvalidationRuleSet = attributeRules->ruleSet.get(); + return; + } + } +} + +void AttributeChangeInvalidation::invalidateDescendants() +{ + if (!m_descendantInvalidationRuleSet) + return; + StyleInvalidationAnalysis invalidationAnalysis(*m_descendantInvalidationRuleSet); + invalidationAnalysis.invalidateStyle(m_element); +} + +} +} diff --git a/Source/WebCore/style/AttributeChangeInvalidation.h b/Source/WebCore/style/AttributeChangeInvalidation.h new file mode 100644 index 000000000..5e2f6db3e --- /dev/null +++ b/Source/WebCore/style/AttributeChangeInvalidation.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Element.h" + +namespace WebCore { + +class RuleSet; + +namespace Style { + +class AttributeChangeInvalidation { +public: + AttributeChangeInvalidation(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue); + ~AttributeChangeInvalidation(); + +private: + void invalidateStyle(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue); + void invalidateDescendants(); + + const bool m_isEnabled; + Element& m_element; + + const RuleSet* m_descendantInvalidationRuleSet { nullptr }; +}; + +inline AttributeChangeInvalidation::AttributeChangeInvalidation(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) + : m_isEnabled(element.needsStyleInvalidation()) + , m_element(element) +{ + if (!m_isEnabled) + return; + invalidateStyle(attributeName, oldValue, newValue); + invalidateDescendants(); +} + +inline AttributeChangeInvalidation::~AttributeChangeInvalidation() +{ + if (!m_isEnabled) + return; + invalidateDescendants(); +} + +} +} diff --git a/Source/WebCore/style/ClassChangeInvalidation.cpp b/Source/WebCore/style/ClassChangeInvalidation.cpp new file mode 100644 index 000000000..e25fc49b4 --- /dev/null +++ b/Source/WebCore/style/ClassChangeInvalidation.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ClassChangeInvalidation.h" + +#include "DocumentRuleSets.h" +#include "ElementChildIterator.h" +#include "ShadowRoot.h" +#include "SpaceSplitString.h" +#include "StyleInvalidationAnalysis.h" +#include "StyleResolver.h" +#include "StyleScope.h" +#include <wtf/BitVector.h> + +namespace WebCore { +namespace Style { + +using ClassChangeVector = Vector<AtomicStringImpl*, 4>; + +static ClassChangeVector collectClasses(const SpaceSplitString& classes) +{ + ClassChangeVector result; + result.reserveCapacity(classes.size()); + for (unsigned i = 0; i < classes.size(); ++i) + result.uncheckedAppend(classes[i].impl()); + return result; +} + +static ClassChangeVector computeClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses) +{ + unsigned oldSize = oldClasses.size(); + unsigned newSize = newClasses.size(); + + if (!oldSize) + return collectClasses(newClasses); + if (!newSize) + return collectClasses(oldClasses); + + ClassChangeVector changedClasses; + + BitVector remainingClassBits; + remainingClassBits.ensureSize(oldSize); + // Class vectors tend to be very short. This is faster than using a hash table. + for (unsigned i = 0; i < newSize; ++i) { + bool foundFromBoth = false; + for (unsigned j = 0; j < oldSize; ++j) { + if (newClasses[i] == oldClasses[j]) { + remainingClassBits.quickSet(j); + foundFromBoth = true; + } + } + if (foundFromBoth) + continue; + changedClasses.append(newClasses[i].impl()); + } + for (unsigned i = 0; i < oldSize; ++i) { + // If the bit is not set the the corresponding class has been removed. + if (remainingClassBits.quickGet(i)) + continue; + changedClasses.append(oldClasses[i].impl()); + } + + return changedClasses; +} + +static bool mayBeAffectedByHostRules(ShadowRoot* shadowRoot, AtomicStringImpl* changedClass) +{ + if (!shadowRoot) + return false; + auto& shadowRuleSets = shadowRoot->styleScope().resolver().ruleSets(); + if (shadowRuleSets.authorStyle().hostPseudoClassRules().isEmpty()) + return false; + return shadowRuleSets.features().classesInRules.contains(changedClass); +} + +static bool mayBeAffectedBySlottedRules(const Vector<ShadowRoot*>& assignedShadowRoots, AtomicStringImpl* changedClass) +{ + for (auto& assignedShadowRoot : assignedShadowRoots) { + auto& ruleSets = assignedShadowRoot->styleScope().resolver().ruleSets(); + if (ruleSets.authorStyle().slottedPseudoElementRules().isEmpty()) + continue; + if (ruleSets.features().classesInRules.contains(changedClass)) + return true; + } + return false; +} + +void ClassChangeInvalidation::invalidateStyle(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses) +{ + auto changedClasses = computeClassChange(oldClasses, newClasses); + + auto& ruleSets = m_element.styleResolver().ruleSets(); + auto* shadowRoot = m_element.shadowRoot(); + auto assignedShadowRoots = assignedShadowRootsIfSlotted(m_element); + + ClassChangeVector changedClassesAffectingStyle; + for (auto* changedClass : changedClasses) { + bool mayAffectStyle = ruleSets.features().classesInRules.contains(changedClass) + || mayBeAffectedByHostRules(shadowRoot, changedClass) + || mayBeAffectedBySlottedRules(assignedShadowRoots, changedClass); + if (mayAffectStyle) + changedClassesAffectingStyle.append(changedClass); + }; + + if (changedClassesAffectingStyle.isEmpty()) + return; + + if (shadowRoot && ruleSets.authorStyle().hasShadowPseudoElementRules()) { + m_element.invalidateStyleForSubtree(); + return; + } + + m_element.invalidateStyle(); + + if (!childrenOfType<Element>(m_element).first()) + return; + + for (auto* changedClass : changedClassesAffectingStyle) { + auto* ancestorClassRules = ruleSets.ancestorClassRules(changedClass); + if (!ancestorClassRules) + continue; + m_descendantInvalidationRuleSets.append(ancestorClassRules); + } +} + +void ClassChangeInvalidation::invalidateDescendantStyle() +{ + for (auto* ancestorClassRules : m_descendantInvalidationRuleSets) { + StyleInvalidationAnalysis invalidationAnalysis(*ancestorClassRules); + invalidationAnalysis.invalidateStyle(m_element); + } +} + +} +} diff --git a/Source/WebCore/style/ClassChangeInvalidation.h b/Source/WebCore/style/ClassChangeInvalidation.h new file mode 100644 index 000000000..62cd75793 --- /dev/null +++ b/Source/WebCore/style/ClassChangeInvalidation.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Element.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class DocumentRuleSets; +class RuleSet; +class SpaceSplitString; + +namespace Style { + +class ClassChangeInvalidation { +public: + ClassChangeInvalidation(Element&, const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses); + ~ClassChangeInvalidation(); + +private: + void invalidateStyle(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses); + void invalidateDescendantStyle(); + + const bool m_isEnabled; + Element& m_element; + + Vector<const RuleSet*, 4> m_descendantInvalidationRuleSets; +}; + +inline ClassChangeInvalidation::ClassChangeInvalidation(Element& element, const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses) + : m_isEnabled(element.needsStyleInvalidation()) + , m_element(element) + +{ + if (!m_isEnabled) + return; + invalidateStyle(oldClasses, newClasses); + invalidateDescendantStyle(); +} + +inline ClassChangeInvalidation::~ClassChangeInvalidation() +{ + if (!m_isEnabled) + return; + invalidateDescendantStyle(); +} + +} +} diff --git a/Source/WebCore/style/IdChangeInvalidation.cpp b/Source/WebCore/style/IdChangeInvalidation.cpp new file mode 100644 index 000000000..a0d99febf --- /dev/null +++ b/Source/WebCore/style/IdChangeInvalidation.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IdChangeInvalidation.h" + +#include "DocumentRuleSets.h" +#include "ElementChildIterator.h" +#include "ShadowRoot.h" +#include "StyleResolver.h" +#include "StyleScope.h" + +namespace WebCore { +namespace Style { + +static bool mayBeAffectedByHostRules(const Element& element, const AtomicString& changedId) +{ + auto* shadowRoot = element.shadowRoot(); + if (!shadowRoot) + return false; + auto& shadowRuleSets = shadowRoot->styleScope().resolver().ruleSets(); + if (shadowRuleSets.authorStyle().hostPseudoClassRules().isEmpty()) + return false; + return shadowRuleSets.features().idsInRules.contains(changedId); +} + +static bool mayBeAffectedBySlottedRules(const Element& element, const AtomicString& changedId) +{ + for (auto* shadowRoot : assignedShadowRootsIfSlotted(element)) { + auto& ruleSets = shadowRoot->styleScope().resolver().ruleSets(); + if (ruleSets.authorStyle().slottedPseudoElementRules().isEmpty()) + continue; + if (ruleSets.features().idsInRules.contains(changedId)) + return true; + } + return false; +} + +void IdChangeInvalidation::invalidateStyle(const AtomicString& changedId) +{ + if (changedId.isEmpty()) + return; + + auto& ruleSets = m_element.styleResolver().ruleSets(); + + bool mayAffectStyle = ruleSets.features().idsInRules.contains(changedId) + || mayBeAffectedByHostRules(m_element, changedId) + || mayBeAffectedBySlottedRules(m_element, changedId); + + if (!mayAffectStyle) + return; + + if (m_element.shadowRoot() && ruleSets.authorStyle().hasShadowPseudoElementRules()) { + m_element.invalidateStyleForSubtree(); + return; + } + + m_element.invalidateStyle(); + + // This could be easily optimized for fine-grained descendant invalidation similar to ClassChangeInvalidation. + // However using ids for dynamic styling is rare and this is probably not worth the memory cost of the required data structures. + bool mayAffectDescendantStyle = ruleSets.features().idsMatchingAncestorsInRules.contains(changedId); + if (mayAffectDescendantStyle) + m_element.invalidateStyleForSubtree(); + else + m_element.invalidateStyle(); +} + +} +} diff --git a/Source/WebCore/style/IdChangeInvalidation.h b/Source/WebCore/style/IdChangeInvalidation.h new file mode 100644 index 000000000..0e3bdef9b --- /dev/null +++ b/Source/WebCore/style/IdChangeInvalidation.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Element.h" + +namespace WebCore { + +namespace Style { + +class IdChangeInvalidation { +public: + IdChangeInvalidation(Element&, const AtomicString& oldId, const AtomicString& newId); + ~IdChangeInvalidation(); + +private: + void invalidateStyle(const AtomicString&); + + const bool m_isEnabled; + Element& m_element; + + AtomicString m_newId; +}; + +inline IdChangeInvalidation::IdChangeInvalidation(Element& element, const AtomicString& oldId, const AtomicString& newId) + : m_isEnabled(element.needsStyleInvalidation()) + , m_element(element) +{ + if (!m_isEnabled) + return; + if (oldId == newId) + return; + m_newId = newId; + invalidateStyle(oldId); +} + +inline IdChangeInvalidation::~IdChangeInvalidation() +{ + if (!m_isEnabled) + return; + invalidateStyle(m_newId); +} + +} +} diff --git a/Source/WebCore/style/InlineTextBoxStyle.cpp b/Source/WebCore/style/InlineTextBoxStyle.cpp new file mode 100644 index 000000000..b411c0891 --- /dev/null +++ b/Source/WebCore/style/InlineTextBoxStyle.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InlineTextBoxStyle.h" + +#include "FontCascade.h" +#include "InlineTextBox.h" +#include "RootInlineBox.h" + +namespace WebCore { + +int computeUnderlineOffset(TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, int textDecorationThickness) +{ + // This represents the gap between the baseline and the closest edge of the underline. + int gap = std::max<int>(1, ceilf(textDecorationThickness / 2.0)); + + // FIXME: The code for visual overflow detection passes in a null inline text box. This means it is now + // broken for the case where auto needs to behave like "under". + + // According to the specification TextUnderlinePositionAuto should avoid drawing through glyphs in + // scripts where it would not be appropriate (e.g., ideographs). + // Strictly speaking this can occur whenever the line contains ideographs + // even if it is horizontal, but detecting this has performance implications. For now we only work with + // vertical text, since we already determined the baseline type to be ideographic in that + // case. + + TextUnderlinePosition resolvedUnderlinePosition = underlinePosition; + if (resolvedUnderlinePosition == TextUnderlinePositionAuto) { + if (inlineTextBox) + resolvedUnderlinePosition = inlineTextBox->root().baselineType() == IdeographicBaseline ? TextUnderlinePositionUnder : TextUnderlinePositionAlphabetic; + else + resolvedUnderlinePosition = TextUnderlinePositionAlphabetic; + } + + switch (resolvedUnderlinePosition) { + case TextUnderlinePositionAlphabetic: + return fontMetrics.ascent() + gap; + case TextUnderlinePositionUnder: { + ASSERT(inlineTextBox); + // Position underline relative to the bottom edge of the lowest element's content box. + const RootInlineBox& rootBox = inlineTextBox->root(); + const RenderElement* decorationRenderer = inlineTextBox->parent()->renderer().enclosingRendererWithTextDecoration(TextDecorationUnderline, inlineTextBox->isFirstLine()); + + float offset; + if (inlineTextBox->renderer().style().isFlippedLinesWritingMode()) { + offset = inlineTextBox->logicalTop(); + rootBox.minLogicalTopForTextDecorationLine(offset, decorationRenderer, TextDecorationUnderline); + offset = inlineTextBox->logicalTop() - offset; + } else { + offset = inlineTextBox->logicalBottom(); + rootBox.maxLogicalBottomForTextDecorationLine(offset, decorationRenderer, TextDecorationUnderline); + offset -= inlineTextBox->logicalBottom(); + } + return inlineTextBox->logicalHeight() + gap + std::max<float>(offset, 0); + } + case TextUnderlinePositionAuto: + ASSERT_NOT_REACHED(); + } + + ASSERT_NOT_REACHED(); + return fontMetrics.ascent() + gap; +} + +void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step) +{ + // Distance between decoration's axis and Bezier curve's control points. + // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since + // the actual curve passes approximately at half of that distance, that is 3 pixels. + // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height + // as strokeThickness increases to make the curve look better. + controlPointDistance = 3 * std::max<float>(2, strokeThickness); + + // Increment used to form the diamond shape between start point (p1), control + // points and end point (p2) along the axis of the decoration. Makes the + // curve wider as strokeThickness increases to make the curve look better. + step = 2 * std::max<float>(2, strokeThickness); +} + +static inline void extendIntToFloat(int& extendMe, float extendTo) +{ + extendMe = std::max(extendMe, static_cast<int>(ceilf(extendTo))); +} + +GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, const InlineTextBox* inlineTextBox) +{ + ASSERT(!inlineTextBox || inlineTextBox->lineStyle() == lineStyle); + + TextDecoration decoration = lineStyle.textDecorationsInEffect(); + if (decoration == TextDecorationNone) + return GlyphOverflow(); + + float strokeThickness = textDecorationStrokeThickness(lineStyle.fontSize()); + float controlPointDistance = 0; + float step; + float wavyOffset = 0; + + TextDecorationStyle decorationStyle = lineStyle.textDecorationStyle(); + float height = lineStyle.fontCascade().fontMetrics().floatHeight(); + GlyphOverflow overflowResult; + + if (decorationStyle == TextDecorationStyleWavy) { + getWavyStrokeParameters(strokeThickness, controlPointDistance, step); + wavyOffset = wavyOffsetFromDecoration(); + overflowResult.left = strokeThickness; + overflowResult.right = strokeThickness; + } + + // These metrics must match where underlines get drawn. + if (decoration & TextDecorationUnderline) { + // Compensate for the integral ceiling in GraphicsContext::computeLineBoundsAndAntialiasingModeForText() + int underlineOffset = 1; + underlineOffset += computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), inlineTextBox, strokeThickness); + if (decorationStyle == TextDecorationStyleWavy) { + extendIntToFloat(overflowResult.bottom, underlineOffset + wavyOffset + controlPointDistance + strokeThickness - height); + extendIntToFloat(overflowResult.top, -(underlineOffset + wavyOffset - controlPointDistance - strokeThickness)); + } else { + extendIntToFloat(overflowResult.bottom, underlineOffset + strokeThickness - height); + extendIntToFloat(overflowResult.top, -underlineOffset); + } + } + if (decoration & TextDecorationOverline) { + if (decorationStyle == TextDecorationStyleWavy) { + extendIntToFloat(overflowResult.bottom, -wavyOffset + controlPointDistance + strokeThickness - height); + extendIntToFloat(overflowResult.top, wavyOffset + controlPointDistance + strokeThickness); + } else { + extendIntToFloat(overflowResult.bottom, strokeThickness - height); + // top is untouched + } + } + if (decoration & TextDecorationLineThrough) { + float baseline = lineStyle.fontMetrics().floatAscent(); + if (decorationStyle == TextDecorationStyleWavy) { + extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + controlPointDistance + strokeThickness - height); + extendIntToFloat(overflowResult.top, -(2 * baseline / 3 - controlPointDistance - strokeThickness)); + } else { + extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + strokeThickness - height); + extendIntToFloat(overflowResult.top, -(2 * baseline / 3)); + } + } + return overflowResult; +} + +} diff --git a/Source/WebCore/style/InlineTextBoxStyle.h b/Source/WebCore/style/InlineTextBoxStyle.h new file mode 100644 index 000000000..a0ed4b5ba --- /dev/null +++ b/Source/WebCore/style/InlineTextBoxStyle.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "FontCascade.h" +#include "RenderStyleConstants.h" + +namespace WebCore { + +class InlineTextBox; +class RenderStyle; + +inline float textDecorationStrokeThickness(float fontSize) +{ + const float textDecorationBaseFontSize = 16; + return fontSize / textDecorationBaseFontSize; +} + +inline float wavyOffsetFromDecoration() +{ + return 2; +} + +GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, const InlineTextBox*); +void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step); +int computeUnderlineOffset(TextUnderlinePosition, const FontMetrics&, const InlineTextBox*, int textDecorationThickness); + +} // namespace WebCore diff --git a/Source/WebCore/style/RenderTreePosition.cpp b/Source/WebCore/style/RenderTreePosition.cpp new file mode 100644 index 000000000..2b90d6847 --- /dev/null +++ b/Source/WebCore/style/RenderTreePosition.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderTreePosition.h" + +#include "ComposedTreeIterator.h" +#include "FlowThreadController.h" +#include "PseudoElement.h" +#include "RenderObject.h" +#include "ShadowRoot.h" + +namespace WebCore { + +void RenderTreePosition::computeNextSibling(const Node& node) +{ + ASSERT(!node.renderer()); + if (m_hasValidNextSibling) { +#if !ASSERT_DISABLED + const unsigned oNSquaredAvoidanceLimit = 20; + bool skipAssert = m_parent.isRenderView() || ++m_assertionLimitCounter > oNSquaredAvoidanceLimit; + ASSERT(skipAssert || nextSiblingRenderer(node) == m_nextSibling); +#endif + return; + } + m_nextSibling = nextSiblingRenderer(node); + m_hasValidNextSibling = true; +} + +void RenderTreePosition::invalidateNextSibling(const RenderObject& siblingRenderer) +{ + if (!m_hasValidNextSibling) + return; + if (m_nextSibling == &siblingRenderer) + m_hasValidNextSibling = false; +} + +RenderObject* RenderTreePosition::previousSiblingRenderer(const Text& textNode) const +{ + if (textNode.renderer()) + return textNode.renderer()->previousSibling(); + + auto* parentElement = m_parent.element(); + + auto composedChildren = composedTreeChildren(*parentElement); + for (auto it = composedChildren.at(textNode), end = composedChildren.end(); it != end; --it) { + RenderObject* renderer = it->renderer(); + if (renderer && !isRendererReparented(*renderer)) + return renderer; + } + if (auto* before = parentElement->beforePseudoElement()) + return before->renderer(); + return nullptr; +} + +RenderObject* RenderTreePosition::nextSiblingRenderer(const Node& node) const +{ + auto* parentElement = m_parent.element(); + if (!parentElement) + return nullptr; + if (node.isAfterPseudoElement()) + return nullptr; + + auto composedDescendants = composedTreeDescendants(*parentElement); + auto it = node.isBeforePseudoElement() ? composedDescendants.begin() : composedDescendants.at(node); + auto end = composedDescendants.end(); + + while (it != end) { + auto& node = *it; + bool hasDisplayContents = is<Element>(node) && downcast<Element>(node).hasDisplayContents(); + if (hasDisplayContents) { + it.traverseNext(); + continue; + } + RenderObject* renderer = node.renderer(); + if (renderer && !isRendererReparented(*renderer)) + return renderer; + + it.traverseNextSkippingChildren(); + } + if (PseudoElement* after = parentElement->afterPseudoElement()) + return after->renderer(); + return nullptr; +} + +#if ENABLE(CSS_REGIONS) +RenderTreePosition RenderTreePosition::insertionPositionForFlowThread(Element* insertionParent, Element& element, const RenderStyle& style) +{ + ASSERT(element.shouldMoveToFlowThread(style)); + auto& parentFlowThread = element.document().renderView()->flowThreadController().ensureRenderFlowThreadWithName(style.flowThread()); + + if (!insertionParent) + return { parentFlowThread, nullptr }; + + auto composedDescendants = composedTreeDescendants(*insertionParent); + auto it = element.isBeforePseudoElement() ? composedDescendants.begin() : composedDescendants.at(element); + auto end = composedDescendants.end(); + while (it != end) { + auto& currentNode = *it; + bool hasDisplayContents = is<Element>(currentNode) && downcast<Element>(currentNode).hasDisplayContents(); + if (hasDisplayContents) { + it.traverseNext(); + continue; + } + + auto* renderer = currentNode.renderer(); + if (!renderer) { + it.traverseNextSkippingChildren(); + continue; + } + + if (!is<RenderElement>(*renderer)) { + it.traverseNext(); + continue; + } + + // We are the last child in this flow. + if (!isRendererReparented(*renderer)) + return { parentFlowThread, nullptr }; + + if (renderer->style().hasFlowInto() && style.flowThread() == renderer->style().flowThread()) + return { parentFlowThread, downcast<RenderElement>(renderer) }; + // Nested flows, skip. + it.traverseNextSkippingChildren(); + } + return { parentFlowThread, nullptr }; +} +#endif + +bool RenderTreePosition::isRendererReparented(const RenderObject& renderer) +{ + if (!renderer.node()->isElementNode()) + return false; + if (renderer.style().hasFlowInto()) + return true; + return false; +} + +} diff --git a/Source/WebCore/style/RenderTreePosition.h b/Source/WebCore/style/RenderTreePosition.h new file mode 100644 index 000000000..47a05926c --- /dev/null +++ b/Source/WebCore/style/RenderTreePosition.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "RenderElement.h" +#include "RenderNamedFlowThread.h" +#include "RenderText.h" +#include "RenderView.h" + +namespace WebCore { + +class RenderTreePosition { +public: + explicit RenderTreePosition(RenderView& root) + : m_parent(root) + , m_hasValidNextSibling(true) + { + } + + explicit RenderTreePosition(RenderElement& parent) + : m_parent(parent) + { + } + +#if ENABLE(CSS_REGIONS) + static RenderTreePosition insertionPositionForFlowThread(Element* insertionParent, Element& child, const RenderStyle&); +#endif + + RenderElement& parent() const { return m_parent; } + void insert(RenderObject&); + bool canInsert(RenderElement&) const; + bool canInsert(RenderText&) const; + + void computeNextSibling(const Node&); + void invalidateNextSibling() { m_hasValidNextSibling = false; } + void invalidateNextSibling(const RenderObject&); + + RenderObject* previousSiblingRenderer(const Text&) const; + RenderObject* nextSiblingRenderer(const Node&) const; + static bool isRendererReparented(const RenderObject&); + +private: +#if ENABLE(CSS_REGIONS) + RenderTreePosition(RenderFlowThread& parent, RenderObject* nextSibling) + : m_parent(parent) + , m_nextSibling(nextSibling) + , m_hasValidNextSibling(true) + { + } +#endif + + RenderElement& m_parent; + RenderObject* m_nextSibling { nullptr }; + bool m_hasValidNextSibling { false }; +#if !ASSERT_DISABLED + unsigned m_assertionLimitCounter { 0 }; +#endif +}; + +inline bool RenderTreePosition::canInsert(RenderElement& renderer) const +{ + ASSERT(!renderer.parent()); + return m_parent.isChildAllowed(renderer, renderer.style()); +} + +inline bool RenderTreePosition::canInsert(RenderText& renderer) const +{ + ASSERT(!renderer.parent()); + return m_parent.isChildAllowed(renderer, m_parent.style()); +} + +inline void RenderTreePosition::insert(RenderObject& renderer) +{ + ASSERT(m_hasValidNextSibling); + m_parent.addChild(&renderer, m_nextSibling); +} + +} // namespace WebCore diff --git a/Source/WebCore/style/RenderTreeUpdater.cpp b/Source/WebCore/style/RenderTreeUpdater.cpp new file mode 100644 index 000000000..65682ff91 --- /dev/null +++ b/Source/WebCore/style/RenderTreeUpdater.cpp @@ -0,0 +1,636 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderTreeUpdater.h" + +#include "AXObjectCache.h" +#include "ComposedTreeAncestorIterator.h" +#include "ComposedTreeIterator.h" +#include "Document.h" +#include "Element.h" +#include "FlowThreadController.h" +#include "HTMLSlotElement.h" +#include "InspectorInstrumentation.h" +#include "NodeRenderStyle.h" +#include "PseudoElement.h" +#include "RenderFullScreen.h" +#include "RenderNamedFlowThread.h" +#include "StyleResolver.h" +#include "StyleTreeResolver.h" + +#if PLATFORM(IOS) +#include "WKContentObservation.h" +#include "WKContentObservationInternal.h" +#endif + +namespace WebCore { + +#if PLATFORM(IOS) +class CheckForVisibilityChange { +public: + CheckForVisibilityChange(const Element&); + ~CheckForVisibilityChange(); + +private: + const Element& m_element; + EDisplay m_previousDisplay; + EVisibility m_previousVisibility; + EVisibility m_previousImplicitVisibility; +}; +#endif // PLATFORM(IOS) + +RenderTreeUpdater::Parent::Parent(ContainerNode& root) + : element(is<Document>(root) ? nullptr : downcast<Element>(&root)) + , renderTreePosition(RenderTreePosition(*root.renderer())) +{ +} + +RenderTreeUpdater::Parent::Parent(Element& element, Style::Change styleChange) + : element(&element) + , styleChange(styleChange) + , renderTreePosition(element.renderer() ? std::make_optional(RenderTreePosition(*element.renderer())) : std::nullopt) +{ +} + + +RenderTreeUpdater::RenderTreeUpdater(Document& document) + : m_document(document) +{ +} + +static ContainerNode* findRenderingRoot(ContainerNode& node) +{ + if (node.renderer()) + return &node; + for (auto& ancestor : composedTreeAncestors(node)) { + if (ancestor.renderer()) + return &ancestor; + if (!ancestor.hasDisplayContents()) + return nullptr; + } + return &node.document(); +} + +static ListHashSet<ContainerNode*> findRenderingRoots(const Style::Update& update) +{ + ListHashSet<ContainerNode*> renderingRoots; + for (auto* root : update.roots()) { + auto* renderingRoot = findRenderingRoot(*root); + if (!renderingRoot) + continue; + renderingRoots.add(renderingRoot); + } + return renderingRoots; +} + +void RenderTreeUpdater::commit(std::unique_ptr<const Style::Update> styleUpdate) +{ + ASSERT(&m_document == &styleUpdate->document()); + + if (!m_document.shouldCreateRenderers() || !m_document.renderView()) + return; + + Style::PostResolutionCallbackDisabler callbackDisabler(m_document); + + m_styleUpdate = WTFMove(styleUpdate); + + for (auto* root : findRenderingRoots(*m_styleUpdate)) + updateRenderTree(*root); + + m_styleUpdate = nullptr; +} + +static bool shouldCreateRenderer(const Element& element, const RenderElement& parentRenderer) +{ + if (!parentRenderer.canHaveChildren() && !(element.isPseudoElement() && parentRenderer.canHaveGeneratedChildren())) + return false; + if (parentRenderer.element() && !parentRenderer.element()->childShouldCreateRenderer(element)) + return false; + return true; +} + +void RenderTreeUpdater::updateRenderTree(ContainerNode& root) +{ + ASSERT(root.renderer()); + ASSERT(m_parentStack.isEmpty()); + + m_parentStack.append(Parent(root)); + + auto descendants = composedTreeDescendants(root); + auto it = descendants.begin(); + auto end = descendants.end(); + + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=156172 + it.dropAssertions(); + + while (it != end) { + popParentsToDepth(it.depth()); + + auto& node = *it; + + if (auto* renderer = node.renderer()) + renderTreePosition().invalidateNextSibling(*renderer); + + if (is<Text>(node)) { + auto& text = downcast<Text>(node); + if (parent().styleChange == Style::Detach || m_styleUpdate->textUpdate(text) || m_invalidatedWhitespaceOnlyTextSiblings.contains(&text)) + updateTextRenderer(text); + + it.traverseNextSkippingChildren(); + continue; + } + + auto& element = downcast<Element>(node); + + auto* elementUpdate = m_styleUpdate->elementUpdate(element); + if (!elementUpdate) { + it.traverseNextSkippingChildren(); + continue; + } + + updateElementRenderer(element, *elementUpdate); + + bool mayHaveRenderedDescendants = element.renderer() || (element.hasDisplayContents() && shouldCreateRenderer(element, renderTreePosition().parent())); + if (!mayHaveRenderedDescendants) { + it.traverseNextSkippingChildren(); + continue; + } + + pushParent(element, elementUpdate ? elementUpdate->change : Style::NoChange); + + it.traverseNext(); + } + + popParentsToDepth(0); + + m_invalidatedWhitespaceOnlyTextSiblings.clear(); +} + +RenderTreePosition& RenderTreeUpdater::renderTreePosition() +{ + for (unsigned i = m_parentStack.size(); i; --i) { + if (auto& position = m_parentStack[i - 1].renderTreePosition) + return *position; + } + ASSERT_NOT_REACHED(); + return *m_parentStack.last().renderTreePosition; +} + +void RenderTreeUpdater::pushParent(Element& element, Style::Change changeType) +{ + m_parentStack.append(Parent(element, changeType)); + + updateBeforeOrAfterPseudoElement(element, BEFORE); +} + +void RenderTreeUpdater::popParent() +{ + auto& parent = m_parentStack.last(); + + if (parent.element) { + updateBeforeOrAfterPseudoElement(*parent.element, AFTER); + + if (parent.element->hasCustomStyleResolveCallbacks() && parent.styleChange == Style::Detach && parent.element->renderer()) + parent.element->didAttachRenderers(); + } + m_parentStack.removeLast(); +} + +void RenderTreeUpdater::popParentsToDepth(unsigned depth) +{ + ASSERT(m_parentStack.size() >= depth); + + while (m_parentStack.size() > depth) + popParent(); +} + +static bool pseudoStyleCacheIsInvalid(RenderElement* renderer, RenderStyle* newStyle) +{ + const RenderStyle& currentStyle = renderer->style(); + + const PseudoStyleCache* pseudoStyleCache = currentStyle.cachedPseudoStyles(); + if (!pseudoStyleCache) + return false; + + for (auto& cache : *pseudoStyleCache) { + PseudoId pseudoId = cache->styleType(); + std::unique_ptr<RenderStyle> newPseudoStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId), newStyle, newStyle); + if (!newPseudoStyle) + return true; + if (*newPseudoStyle != *cache) { + newStyle->addCachedPseudoStyle(WTFMove(newPseudoStyle)); + return true; + } + } + return false; +} + +void RenderTreeUpdater::updateElementRenderer(Element& element, const Style::ElementUpdate& update) +{ +#if PLATFORM(IOS) + CheckForVisibilityChange checkForVisibilityChange(element); +#endif + + bool shouldTearDownRenderers = update.change == Style::Detach + && (element.renderer() || element.isNamedFlowContentElement() || element.hasDisplayContents()); + if (shouldTearDownRenderers) { + if (!element.renderer()) { + // We may be tearing down a descendant renderer cached in renderTreePosition. + renderTreePosition().invalidateNextSibling(); + } + tearDownRenderers(element, TeardownType::KeepHoverAndActive); + } + + bool hasDisplayContents = update.style->display() == CONTENTS; + if (hasDisplayContents != element.hasDisplayContents()) { + element.setHasDisplayContents(hasDisplayContents); + // Render tree position needs to be recomputed as rendering siblings may be found from the display:contents subtree. + renderTreePosition().invalidateNextSibling(); + } + + bool shouldCreateNewRenderer = !element.renderer() && !hasDisplayContents; + if (shouldCreateNewRenderer) { + if (element.hasCustomStyleResolveCallbacks()) + element.willAttachRenderers(); + createRenderer(element, RenderStyle::clone(*update.style)); + invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(element); + return; + } + + if (!element.renderer()) + return; + auto& renderer = *element.renderer(); + + if (update.recompositeLayer) { + renderer.setStyle(RenderStyle::clone(*update.style), StyleDifferenceRecompositeLayer); + return; + } + + if (update.change == Style::NoChange) { + if (pseudoStyleCacheIsInvalid(&renderer, update.style.get())) { + renderer.setStyle(RenderStyle::clone(*update.style), StyleDifferenceEqual); + return; + } + return; + } + + renderer.setStyle(RenderStyle::clone(*update.style), StyleDifferenceEqual); +} + +#if ENABLE(CSS_REGIONS) +static void registerElementForFlowThreadIfNeeded(Element& element, const RenderStyle& style) +{ + if (!element.shouldMoveToFlowThread(style)) + return; + FlowThreadController& flowThreadController = element.document().renderView()->flowThreadController(); + flowThreadController.registerNamedFlowContentElement(element, flowThreadController.ensureRenderFlowThreadWithName(style.flowThread())); +} +#endif + +void RenderTreeUpdater::createRenderer(Element& element, RenderStyle&& style) +{ + auto computeInsertionPosition = [this, &element, &style] () { +#if ENABLE(CSS_REGIONS) + if (element.shouldMoveToFlowThread(style)) + return RenderTreePosition::insertionPositionForFlowThread(renderTreePosition().parent().element(), element, style); +#endif + renderTreePosition().computeNextSibling(element); + return renderTreePosition(); + }; + + if (!shouldCreateRenderer(element, renderTreePosition().parent())) + return; + +#if ENABLE(CSS_REGIONS) + // Even display: none elements need to be registered in FlowThreadController. + registerElementForFlowThreadIfNeeded(element, style); +#endif + + if (!element.rendererIsNeeded(style)) + return; + + RenderTreePosition insertionPosition = computeInsertionPosition(); + RenderElement* newRenderer = element.createElementRenderer(WTFMove(style), insertionPosition).leakPtr(); + if (!newRenderer) + return; + if (!insertionPosition.canInsert(*newRenderer)) { + newRenderer->destroy(); + return; + } + + // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style + // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail. + newRenderer->setFlowThreadState(insertionPosition.parent().flowThreadState()); + + element.setRenderer(newRenderer); + + auto& initialStyle = newRenderer->style(); + std::unique_ptr<RenderStyle> animatedStyle; + newRenderer->animation().updateAnimations(*newRenderer, initialStyle, animatedStyle); + if (animatedStyle) { + newRenderer->setStyleInternal(WTFMove(*animatedStyle)); + newRenderer->setHasInitialAnimatedStyle(true); + } + + newRenderer->initializeStyle(); + +#if ENABLE(FULLSCREEN_API) + if (m_document.webkitIsFullScreen() && m_document.webkitCurrentFullScreenElement() == &element) { + newRenderer = RenderFullScreen::wrapRenderer(newRenderer, &insertionPosition.parent(), m_document); + if (!newRenderer) + return; + } +#endif + // Note: Adding newRenderer instead of renderer(). renderer() may be a child of newRenderer. + insertionPosition.insert(*newRenderer); + + if (AXObjectCache* cache = m_document.axObjectCache()) + cache->updateCacheAfterNodeIsAttached(&element); +} + +static bool textRendererIsNeeded(const Text& textNode, const RenderTreePosition& renderTreePosition) +{ + const RenderElement& parentRenderer = renderTreePosition.parent(); + if (!parentRenderer.canHaveChildren()) + return false; + if (parentRenderer.element() && !parentRenderer.element()->childShouldCreateRenderer(textNode)) + return false; + if (textNode.isEditingText()) + return true; + if (!textNode.length()) + return false; + if (!textNode.containsOnlyWhitespace()) + return true; + // This text node has nothing but white space. We may still need a renderer in some cases. + if (parentRenderer.isTable() || parentRenderer.isTableRow() || parentRenderer.isTableSection() || parentRenderer.isRenderTableCol() || parentRenderer.isFrameSet()) + return false; + if (parentRenderer.style().preserveNewline()) // pre/pre-wrap/pre-line always make renderers. + return true; + + RenderObject* previousRenderer = renderTreePosition.previousSiblingRenderer(textNode); + if (previousRenderer && previousRenderer->isBR()) // <span><br/> <br/></span> + return false; + + if (parentRenderer.isRenderInline()) { + // <span><div/> <div/></span> + if (previousRenderer && !previousRenderer->isInline()) + return false; + } else { + if (parentRenderer.isRenderBlock() && !parentRenderer.childrenInline() && (!previousRenderer || !previousRenderer->isInline())) + return false; + + RenderObject* first = parentRenderer.firstChild(); + while (first && first->isFloatingOrOutOfFlowPositioned()) + first = first->nextSibling(); + RenderObject* nextRenderer = renderTreePosition.nextSiblingRenderer(textNode); + if (!first || nextRenderer == first) { + // Whitespace at the start of a block just goes away. Don't even make a render object for this text. + return false; + } + } + return true; +} + +static void createTextRenderer(Text& textNode, RenderTreePosition& renderTreePosition) +{ + ASSERT(!textNode.renderer()); + + auto newRenderer = textNode.createTextRenderer(renderTreePosition.parent().style()); + ASSERT(newRenderer); + + renderTreePosition.computeNextSibling(textNode); + + if (!renderTreePosition.canInsert(*newRenderer)) + return; + + textNode.setRenderer(newRenderer.get()); + renderTreePosition.insert(*newRenderer.leakPtr()); +} + +void RenderTreeUpdater::updateTextRenderer(Text& text) +{ + bool hasRenderer = text.renderer(); + bool needsRenderer = textRendererIsNeeded(text, renderTreePosition()); + if (hasRenderer) { + if (needsRenderer) + return; + tearDownRenderer(text); + invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text); + return; + } + if (!needsRenderer) + return; + createTextRenderer(text, renderTreePosition()); + invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text); +} + +void RenderTreeUpdater::invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(Node& current) +{ + // FIXME: This needs to traverse in composed tree order. + + // This function finds sibling text renderers where the results of textRendererIsNeeded may have changed as a result of + // the current node gaining or losing the renderer. This can only affect white space text nodes. + for (Node* sibling = current.nextSibling(); sibling; sibling = sibling->nextSibling()) { + if (is<Element>(*sibling)) { + if (m_styleUpdate->elementUpdate(downcast<Element>(*sibling))) + return; + // Text renderers beyond rendered elements can't be affected. + if (!sibling->renderer() || RenderTreePosition::isRendererReparented(*sibling->renderer())) + continue; + return; + } + if (!is<Text>(*sibling)) + continue; + Text& textSibling = downcast<Text>(*sibling); + if (m_styleUpdate->textUpdate(textSibling)) + return; + if (!textSibling.containsOnlyWhitespace()) + continue; + m_invalidatedWhitespaceOnlyTextSiblings.add(&textSibling); + } +} + +static bool needsPseudoElement(Element& current, PseudoId pseudoId) +{ + if (!current.renderer() || !current.renderer()->canHaveGeneratedChildren()) + return false; + if (current.isPseudoElement()) + return false; + if (!pseudoElementRendererIsNeeded(current.renderer()->getCachedPseudoStyle(pseudoId))) + return false; + return true; +} + +void RenderTreeUpdater::updateBeforeOrAfterPseudoElement(Element& current, PseudoId pseudoId) +{ + PseudoElement* pseudoElement = pseudoId == BEFORE ? current.beforePseudoElement() : current.afterPseudoElement(); + + if (auto* renderer = pseudoElement ? pseudoElement->renderer() : nullptr) + renderTreePosition().invalidateNextSibling(*renderer); + + bool needsPseudoElement = WebCore::needsPseudoElement(current, pseudoId); + if (!needsPseudoElement) { + if (pseudoElement) { + if (pseudoId == BEFORE) + current.clearBeforePseudoElement(); + else + current.clearAfterPseudoElement(); + } + return; + } + + RefPtr<PseudoElement> newPseudoElement; + if (!pseudoElement) { + newPseudoElement = PseudoElement::create(current, pseudoId); + pseudoElement = newPseudoElement.get(); + } + + auto newStyle = RenderStyle::clonePtr(*current.renderer()->getCachedPseudoStyle(pseudoId, ¤t.renderer()->style())); + + auto elementUpdate = Style::TreeResolver::createAnimatedElementUpdate(WTFMove(newStyle), *pseudoElement, Style::NoChange); + + if (elementUpdate.change == Style::NoChange) + return; + + if (newPseudoElement) { + InspectorInstrumentation::pseudoElementCreated(m_document.page(), *newPseudoElement); + if (pseudoId == BEFORE) + current.setBeforePseudoElement(newPseudoElement.releaseNonNull()); + else + current.setAfterPseudoElement(newPseudoElement.releaseNonNull()); + } + + updateElementRenderer(*pseudoElement, elementUpdate); + + if (elementUpdate.change == Style::Detach) + pseudoElement->didAttachRenderers(); + else + pseudoElement->didRecalcStyle(elementUpdate.change); +} + +void RenderTreeUpdater::tearDownRenderers(Element& root, TeardownType teardownType) +{ + WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; + + Vector<Element*, 30> teardownStack; + + auto push = [&] (Element& element) { + if (element.hasCustomStyleResolveCallbacks()) + element.willDetachRenderers(); + teardownStack.append(&element); + }; + + auto pop = [&] (unsigned depth) { + while (teardownStack.size() > depth) { + auto& element = *teardownStack.takeLast(); + + if (teardownType != TeardownType::KeepHoverAndActive) + element.clearHoverAndActiveStatusBeforeDetachingRenderer(); + element.clearStyleDerivedDataBeforeDetachingRenderer(); + + if (auto* renderer = element.renderer()) { + renderer->destroyAndCleanupAnonymousWrappers(); + element.setRenderer(nullptr); + } + if (element.hasCustomStyleResolveCallbacks()) + element.didDetachRenderers(); + } + }; + + push(root); + + auto descendants = composedTreeDescendants(root); + for (auto it = descendants.begin(), end = descendants.end(); it != end; ++it) { + pop(it.depth()); + + if (is<Text>(*it)) { + tearDownRenderer(downcast<Text>(*it)); + continue; + } + + push(downcast<Element>(*it)); + } + + pop(0); +} + +void RenderTreeUpdater::tearDownRenderer(Text& text) +{ + auto* renderer = text.renderer(); + if (!renderer) + return; + renderer->destroyAndCleanupAnonymousWrappers(); + text.setRenderer(nullptr); +} + +#if PLATFORM(IOS) +static EVisibility elementImplicitVisibility(const Element& element) +{ + auto* renderer = element.renderer(); + if (!renderer) + return VISIBLE; + + auto& style = renderer->style(); + + auto width = style.width(); + auto height = style.height(); + if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0)) + return HIDDEN; + + auto top = style.top(); + auto left = style.left(); + if (left.isFixed() && width.isFixed() && -left.value() >= width.value()) + return HIDDEN; + + if (top.isFixed() && height.isFixed() && -top.value() >= height.value()) + return HIDDEN; + return VISIBLE; +} + +CheckForVisibilityChange::CheckForVisibilityChange(const Element& element) + : m_element(element) + , m_previousDisplay(element.renderStyle() ? element.renderStyle()->display() : NONE) + , m_previousVisibility(element.renderStyle() ? element.renderStyle()->visibility() : HIDDEN) + , m_previousImplicitVisibility(WKObservingContentChanges() && WKObservedContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE) +{ +} + +CheckForVisibilityChange::~CheckForVisibilityChange() +{ + if (!WKObservingContentChanges()) + return; + if (m_element.isInUserAgentShadowTree()) + return; + auto* style = m_element.renderStyle(); + if (!style) + return; + if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN) + || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element) == VISIBLE)) + WKSetObservedContentChange(WKContentVisibilityChange); +} +#endif + +} diff --git a/Source/WebCore/style/RenderTreeUpdater.h b/Source/WebCore/style/RenderTreeUpdater.h new file mode 100644 index 000000000..6d7780dc0 --- /dev/null +++ b/Source/WebCore/style/RenderTreeUpdater.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "RenderTreePosition.h" +#include "StyleChange.h" +#include "StyleUpdate.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class ContainerNode; +class Document; +class Element; +class Node; +class RenderStyle; +class Text; + +class RenderTreeUpdater { +public: + RenderTreeUpdater(Document&); + + void commit(std::unique_ptr<const Style::Update>); + + enum class TeardownType { Normal, KeepHoverAndActive }; + static void tearDownRenderers(Element&, TeardownType = TeardownType::Normal); + static void tearDownRenderer(Text&); + +private: + void updateRenderTree(ContainerNode& root); + void updateTextRenderer(Text&); + void updateElementRenderer(Element&, const Style::ElementUpdate&); + void createRenderer(Element&, RenderStyle&&); + void invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(Node&); + void updateBeforeOrAfterPseudoElement(Element&, PseudoId); + + struct Parent { + Element* element { nullptr }; + Style::Change styleChange { Style::NoChange }; + std::optional<RenderTreePosition> renderTreePosition; + + Parent(ContainerNode& root); + Parent(Element&, Style::Change); + }; + Parent& parent() { return m_parentStack.last(); } + RenderTreePosition& renderTreePosition(); + + void pushParent(Element&, Style::Change); + void popParent(); + void popParentsToDepth(unsigned depth); + + Document& m_document; + std::unique_ptr<const Style::Update> m_styleUpdate; + + Vector<Parent> m_parentStack; + + HashSet<Text*> m_invalidatedWhitespaceOnlyTextSiblings; +}; + +} // namespace WebCore diff --git a/Source/WebCore/style/StyleChange.cpp b/Source/WebCore/style/StyleChange.cpp new file mode 100644 index 000000000..ac850acd2 --- /dev/null +++ b/Source/WebCore/style/StyleChange.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StyleChange.h" + +#include "RenderStyle.h" + +namespace WebCore { +namespace Style { + +Change determineChange(const RenderStyle& s1, const RenderStyle& s2) +{ + if (s1.display() != s2.display()) + return Detach; + if (s1.hasPseudoStyle(FIRST_LETTER) != s2.hasPseudoStyle(FIRST_LETTER)) + return Detach; + // We just detach if a renderer acquires or loses a column-span, since spanning elements + // typically won't contain much content. + if (s1.columnSpan() != s2.columnSpan()) + return Detach; + if (!s1.contentDataEquivalent(&s2)) + return Detach; + // When text-combine property has been changed, we need to prepare a separate renderer object. + // When text-combine is on, we use RenderCombineText, otherwise RenderText. + // https://bugs.webkit.org/show_bug.cgi?id=55069 + if (s1.hasTextCombine() != s2.hasTextCombine()) + return Detach; + // We need to reattach the node, so that it is moved to the correct RenderFlowThread. + if (s1.flowThread() != s2.flowThread()) + return Detach; + // When the region thread has changed, we need to prepare a separate render region object. + if (s1.regionThread() != s2.regionThread()) + return Detach; + // FIXME: Multicolumn regions not yet supported (http://dev.w3.org/csswg/css-regions/#multi-column-regions) + // When the node has region style and changed its multicol style, we have to prepare + // a separate render region object. + if (s1.hasFlowFrom() && (s1.specifiesColumns() != s2.specifiesColumns())) + return Detach; + + if (s1 != s2) { + if (s1.inheritedNotEqual(&s2)) + return Inherit; + + return NoInherit; + } + // If the pseudoStyles have changed, we want any StyleChange that is not NoChange + // because setStyle will do the right thing with anything else. + if (s1.hasAnyPublicPseudoStyles()) { + for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < FIRST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) { + if (s1.hasPseudoStyle(pseudoId)) { + RenderStyle* ps2 = s2.getCachedPseudoStyle(pseudoId); + if (!ps2) + return NoInherit; + RenderStyle* ps1 = s1.getCachedPseudoStyle(pseudoId); + if (!ps1 || *ps1 != *ps2) + return NoInherit; + } + } + } + + return NoChange; +} + +} +} diff --git a/Source/WebCore/style/StyleResolveTree.h b/Source/WebCore/style/StyleChange.h index 55aad0505..de54cbb21 100644 --- a/Source/WebCore/style/StyleResolveTree.h +++ b/Source/WebCore/style/StyleChange.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,32 +23,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef StyleResolveTree_h -#define StyleResolveTree_h +#pragma once namespace WebCore { -class Document; -class Element; class RenderStyle; -class Settings; -class Text; namespace Style { -enum Change { NoChange, NoInherit, Inherit, Detach, Force }; +enum Change { NoChange, NoInherit, Inherit, Force, Detach }; -void resolveTree(Document&, Change); - -void detachRenderTree(Element&); -void detachTextRenderer(Text&); - -void updateTextRendererAfterContentChange(Text&, unsigned offsetOfReplacedData, unsigned lengthOfReplacedData); - -Change determineChange(const RenderStyle*, const RenderStyle*); +Change determineChange(const RenderStyle&, const RenderStyle&); } - } - -#endif diff --git a/Source/WebCore/style/StyleFontSizeFunctions.cpp b/Source/WebCore/style/StyleFontSizeFunctions.cpp index 0ab3e163f..c92cc4c40 100644 --- a/Source/WebCore/style/StyleFontSizeFunctions.cpp +++ b/Source/WebCore/style/StyleFontSizeFunctions.cpp @@ -39,9 +39,13 @@ namespace WebCore { namespace Style { -enum ESmartMinimumForFontSize { DoNotUseSmartMinimumForFontSize, UseSmartMinimumForFontFize }; +enum MinimumFontSizeRule { + DoNotApplyMinimumFontSize, + DoNotUseSmartMinimumForFontSize, + UseSmartMinimumForFontFize +}; -static float computedFontSizeFromSpecifiedSize(float specifiedSize, bool isAbsoluteSize, float zoomFactor, ESmartMinimumForFontSize useSmartMinimumForFontSize, const Settings* settings) +static float computedFontSizeFromSpecifiedSize(float specifiedSize, bool isAbsoluteSize, float zoomFactor, MinimumFontSizeRule minimumSizeRule, const Settings& settings) { // Text with a 0px font size should not be visible and therefore needs to be // exempt from minimum font size rules. Acid3 relies on this for pixel-perfect @@ -60,11 +64,11 @@ static float computedFontSizeFromSpecifiedSize(float specifiedSize, bool isAbsol // However we always allow the page to set an explicit pixel size that is smaller, // since sites will mis-render otherwise (e.g., http://www.gamespot.com with a 9px minimum). - if (!settings) - return 1.0f; + if (minimumSizeRule == DoNotApplyMinimumFontSize) + return specifiedSize; - int minSize = settings->minimumFontSize(); - int minLogicalSize = settings->minimumLogicalFontSize(); + int minSize = settings.minimumFontSize(); + int minLogicalSize = settings.minimumLogicalFontSize(); float zoomedSize = specifiedSize * zoomFactor; // Apply the hard minimum first. We only apply the hard minimum if after zooming we're still too small. @@ -75,7 +79,7 @@ static float computedFontSizeFromSpecifiedSize(float specifiedSize, bool isAbsol // after zooming. The font size must either be relative to the user default or the original size // must have been acceptable. In other words, we only apply the smart minimum whenever we're positive // doing so won't disrupt the layout. - if (useSmartMinimumForFontSize && zoomedSize < minLogicalSize && (specifiedSize >= minLogicalSize || !isAbsoluteSize)) + if (minimumSizeRule == UseSmartMinimumForFontFize && zoomedSize < minLogicalSize && (specifiedSize >= minLogicalSize || !isAbsoluteSize)) zoomedSize = minLogicalSize; // Also clamp to a reasonable maximum to prevent insane font sizes from causing crashes on various @@ -88,10 +92,11 @@ float computedFontSizeFromSpecifiedSize(float specifiedSize, bool isAbsoluteSize float zoomFactor = 1.0f; if (!useSVGZoomRules) { zoomFactor = style->effectiveZoom(); - if (Frame* frame = document.frame()) + Frame* frame = document.frame(); + if (frame && style->textZoom() != TextZoomReset) zoomFactor *= frame->textZoomFactor(); } - return computedFontSizeFromSpecifiedSize(specifiedSize, isAbsoluteSize, zoomFactor, UseSmartMinimumForFontFize, document.settings()); + return computedFontSizeFromSpecifiedSize(specifiedSize, isAbsoluteSize, zoomFactor, useSVGZoomRules ? DoNotApplyMinimumFontSize : UseSmartMinimumForFontFize, document.settings()); } float computedFontSizeFromSpecifiedSizeForSVGInlineText(float specifiedSize, bool isAbsoluteSize, float zoomFactor, const Document& document) @@ -143,12 +148,8 @@ static const float fontSizeFactors[totalKeywords] = { 0.60f, 0.75f, 0.89f, 1.0f, float fontSizeForKeyword(unsigned keywordID, bool shouldUseFixedDefaultSize, const Document& document) { - Settings* settings = document.settings(); - if (!settings) - return 1.0f; - bool quirksMode = document.inQuirksMode(); - int mediumSize = shouldUseFixedDefaultSize ? settings->defaultFixedFontSize() : settings->defaultFontSize(); + int mediumSize = shouldUseFixedDefaultSize ? document.settings().defaultFixedFontSize() : document.settings().defaultFontSize(); if (mediumSize >= fontSizeTableMin && mediumSize <= fontSizeTableMax) { // Look up the entry in the table. int row = mediumSize - fontSizeTableMin; @@ -157,7 +158,7 @@ float fontSizeForKeyword(unsigned keywordID, bool shouldUseFixedDefaultSize, con } // Value is outside the range of the table. Apply the scale factor instead. - float minLogicalSize = std::max(settings->minimumLogicalFontSize(), 1); + float minLogicalSize = std::max(document.settings().minimumLogicalFontSize(), 1); return std::max(fontSizeFactors[keywordID - CSSValueXxSmall] * mediumSize, minLogicalSize); } @@ -174,12 +175,8 @@ static int findNearestLegacyFontSize(int pixelFontSize, const T* table, int mult int legacyFontSizeForPixelSize(int pixelFontSize, bool shouldUseFixedDefaultSize, const Document& document) { - Settings* settings = document.settings(); - if (!settings) - return 1; - bool quirksMode = document.inQuirksMode(); - int mediumSize = shouldUseFixedDefaultSize ? settings->defaultFixedFontSize() : settings->defaultFontSize(); + int mediumSize = shouldUseFixedDefaultSize ? document.settings().defaultFixedFontSize() : document.settings().defaultFontSize(); if (mediumSize >= fontSizeTableMin && mediumSize <= fontSizeTableMax) { int row = mediumSize - fontSizeTableMin; return findNearestLegacyFontSize<int>(pixelFontSize, quirksMode ? quirksFontSizeTable[row] : strictFontSizeTable[row], 1); diff --git a/Source/WebCore/style/StyleFontSizeFunctions.h b/Source/WebCore/style/StyleFontSizeFunctions.h index 0d91434ee..dc23c7d4e 100644 --- a/Source/WebCore/style/StyleFontSizeFunctions.h +++ b/Source/WebCore/style/StyleFontSizeFunctions.h @@ -23,18 +23,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef StyleFontSizeFunctions_h -#define StyleFontSizeFunctions_h +#pragma once -#include "FontOrientation.h" -#include "NonCJKGlyphOrientation.h" -#include <wtf/PassRefPtr.h> +#include "TextFlags.h" namespace WebCore { class Document; class RenderStyle; -class Settings; namespace Style { @@ -50,5 +46,3 @@ int legacyFontSizeForPixelSize(int pixelFontSize, bool shouldUseFixedDefaultSize } } - -#endif diff --git a/Source/WebCore/style/StylePendingResources.cpp b/Source/WebCore/style/StylePendingResources.cpp new file mode 100644 index 000000000..10319f823 --- /dev/null +++ b/Source/WebCore/style/StylePendingResources.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StylePendingResources.h" + +#include "CSSCursorImageValue.h" +#include "CachedResourceLoader.h" +#include "ContentData.h" +#include "CursorData.h" +#include "CursorList.h" +#include "Document.h" +#include "RenderStyle.h" +#include "SVGURIReference.h" +#include "StyleCachedImage.h" +#include "StyleGeneratedImage.h" +#include "TransformFunctions.h" + +namespace WebCore { +namespace Style { + +enum class LoadPolicy { Normal, ShapeOutside }; +static void loadPendingImage(Document& document, const StyleImage* styleImage, const Element* element, LoadPolicy loadPolicy = LoadPolicy::Normal) +{ + if (!styleImage || !styleImage->isPending()) + return; + + ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); + options.contentSecurityPolicyImposition = element && element->isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck; + + // FIXME: Why does shape-outside have different policy than other properties? + if (loadPolicy == LoadPolicy::ShapeOutside) { + options.mode = FetchOptions::Mode::Cors; + options.allowCredentials = DoNotAllowStoredCredentials; + options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set; + } + + const_cast<StyleImage&>(*styleImage).load(document.cachedResourceLoader(), options); +} + +void loadPendingResources(RenderStyle& style, Document& document, const Element* element) +{ + for (auto* backgroundLayer = &style.backgroundLayers(); backgroundLayer; backgroundLayer = backgroundLayer->next()) + loadPendingImage(document, backgroundLayer->image(), element); + + for (auto* contentData = style.contentData(); contentData; contentData = contentData->next()) { + if (is<ImageContentData>(*contentData)) { + auto& styleImage = downcast<ImageContentData>(*contentData).image(); + loadPendingImage(document, &styleImage, element); + } + } + + if (auto* cursorList = style.cursors()) { + for (size_t i = 0; i < cursorList->size(); ++i) + loadPendingImage(document, cursorList->at(i).image(), element); + } + + loadPendingImage(document, style.listStyleImage(), element); + loadPendingImage(document, style.borderImageSource(), element); + loadPendingImage(document, style.maskBoxImageSource(), element); + + if (auto* reflection = style.boxReflect()) + loadPendingImage(document, reflection->mask().image(), element); + + for (auto* maskLayer = &style.maskLayers(); maskLayer; maskLayer = maskLayer->next()) + loadPendingImage(document, maskLayer->image(), element); + + if (style.shapeOutside()) + loadPendingImage(document, style.shapeOutside()->image(), element, LoadPolicy::ShapeOutside); +} + +} +} diff --git a/Source/WebCore/style/StylePendingResources.h b/Source/WebCore/style/StylePendingResources.h new file mode 100644 index 000000000..18ecdc017 --- /dev/null +++ b/Source/WebCore/style/StylePendingResources.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +class Document; +class Element; +class RenderStyle; + +namespace Style { + +void loadPendingResources(RenderStyle&, Document&, const Element*); + +} +} diff --git a/Source/WebCore/style/StyleRelations.cpp b/Source/WebCore/style/StyleRelations.cpp new file mode 100644 index 000000000..adf16566a --- /dev/null +++ b/Source/WebCore/style/StyleRelations.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StyleRelations.h" + +#include "Element.h" +#include "NodeRenderStyle.h" +#include "RenderStyle.h" +#include "StyleUpdate.h" + +namespace WebCore { +namespace Style { + +std::unique_ptr<Relations> commitRelationsToRenderStyle(RenderStyle& style, const Element& element, const Relations& relations) +{ + std::unique_ptr<Relations> remainingRelations; + + auto appendStyleRelation = [&remainingRelations] (const Relation& relation) { + if (!remainingRelations) + remainingRelations = std::make_unique<Relations>(); + remainingRelations->append(relation); + }; + + for (auto& relation : relations) { + if (relation.element != &element) { + appendStyleRelation(relation); + continue; + } + switch (relation.type) { + case Relation::AffectedByActive: + style.setAffectedByActive(); + appendStyleRelation(relation); + break; + case Relation::AffectedByDrag: + style.setAffectedByDrag(); + break; + case Relation::AffectedByEmpty: + style.setEmptyState(relation.value); + appendStyleRelation(relation); + break; + case Relation::AffectedByHover: + style.setAffectedByHover(); + break; + case Relation::FirstChild: + style.setFirstChildState(); + break; + case Relation::LastChild: + style.setLastChildState(); + break; + case Relation::Unique: + style.setUnique(); + break; + case Relation::AffectedByFocusWithin: + case Relation::AffectedByPreviousSibling: + case Relation::AffectsNextSibling: + case Relation::ChildrenAffectedByBackwardPositionalRules: + case Relation::ChildrenAffectedByFirstChildRules: + case Relation::ChildrenAffectedByPropertyBasedBackwardPositionalRules: + case Relation::ChildrenAffectedByLastChildRules: + case Relation::NthChildIndex: + appendStyleRelation(relation); + break; + } + } + return remainingRelations; +} + +void commitRelations(std::unique_ptr<Relations> relations, Update& update) +{ + if (!relations) + return; + for (auto& relation : *relations) { + auto& element = const_cast<Element&>(*relation.element); + switch (relation.type) { + case Relation::AffectedByActive: + element.setStyleAffectedByActive(); + break; + case Relation::AffectedByDrag: + element.setChildrenAffectedByDrag(); + break; + case Relation::AffectedByEmpty: + element.setStyleAffectedByEmpty(); + break; + case Relation::AffectedByFocusWithin: + element.setStyleAffectedByFocusWithin(); + break; + case Relation::AffectedByHover: + element.setChildrenAffectedByHover(); + break; + case Relation::AffectedByPreviousSibling: + element.setStyleIsAffectedByPreviousSibling(); + break; + case Relation::AffectsNextSibling: { + auto* sibling = &element; + for (unsigned i = 0; i < relation.value && sibling; ++i, sibling = sibling->nextElementSibling()) + sibling->setAffectsNextSiblingElementStyle(); + break; + } + case Relation::ChildrenAffectedByBackwardPositionalRules: + element.setChildrenAffectedByBackwardPositionalRules(); + break; + case Relation::ChildrenAffectedByFirstChildRules: + element.setChildrenAffectedByFirstChildRules(); + break; + case Relation::ChildrenAffectedByPropertyBasedBackwardPositionalRules: + element.setChildrenAffectedByBackwardPositionalRules(); + element.setChildrenAffectedByPropertyBasedBackwardPositionalRules(); + break; + case Relation::ChildrenAffectedByLastChildRules: + element.setChildrenAffectedByLastChildRules(); + break; + case Relation::FirstChild: + if (auto* style = update.elementStyle(element)) + style->setFirstChildState(); + break; + case Relation::LastChild: + if (auto* style = update.elementStyle(element)) + style->setLastChildState(); + break; + case Relation::NthChildIndex: + if (auto* style = update.elementStyle(element)) + style->setUnique(); + element.setChildIndex(relation.value); + break; + case Relation::Unique: + if (auto* style = update.elementStyle(element)) + style->setUnique(); + break; + } + } +} + +} +} diff --git a/Source/WebCore/style/StyleRelations.h b/Source/WebCore/style/StyleRelations.h new file mode 100644 index 000000000..831099dd8 --- /dev/null +++ b/Source/WebCore/style/StyleRelations.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <wtf/Vector.h> + +namespace WebCore { + +class Element; +class RenderStyle; + +namespace Style { + +class Update; + +struct Relation { + enum Type { + AffectedByActive, + AffectedByDrag, + AffectedByEmpty, + AffectedByFocusWithin, + AffectedByHover, + AffectedByPreviousSibling, + // For AffectsNextSibling 'value' tells how many element siblings to mark starting with 'element'. + AffectsNextSibling, + ChildrenAffectedByBackwardPositionalRules, + ChildrenAffectedByFirstChildRules, + ChildrenAffectedByPropertyBasedBackwardPositionalRules, + ChildrenAffectedByLastChildRules, + FirstChild, + LastChild, + NthChildIndex, + Unique, + }; + const Element* element; + Type type; + unsigned value; + + Relation(const Element& element, Type type, unsigned value = 1) + : element(&element) + , type(type) + , value(value) + { } +}; + +using Relations = Vector<Relation, 8>; + +std::unique_ptr<Relations> commitRelationsToRenderStyle(RenderStyle&, const Element&, const Relations&); +void commitRelations(std::unique_ptr<Relations>, Update&); + +} +} diff --git a/Source/WebCore/style/StyleResolveForDocument.cpp b/Source/WebCore/style/StyleResolveForDocument.cpp index c920829c2..1d5ac769b 100644 --- a/Source/WebCore/style/StyleResolveForDocument.cpp +++ b/Source/WebCore/style/StyleResolveForDocument.cpp @@ -36,6 +36,7 @@ #include "HTMLIFrameElement.h" #include "LocaleToScriptMapping.h" #include "NodeRenderStyle.h" +#include "Page.h" #include "RenderObject.h" #include "RenderStyle.h" #include "RenderView.h" @@ -47,95 +48,81 @@ namespace WebCore { namespace Style { -PassRef<RenderStyle> resolveForDocument(const Document& document) +RenderStyle resolveForDocument(const Document& document) { ASSERT(document.hasLivingRenderTree()); RenderView& renderView = *document.renderView(); - // HTML5 states that seamless iframes should replace default CSS values - // with values inherited from the containing iframe element. However, - // some values (such as the case of designMode = "on") still need to - // be set by this "document style". auto documentStyle = RenderStyle::create(); - bool seamlessWithParent = document.shouldDisplaySeamlesslyWithParent(); - if (seamlessWithParent) { - RenderStyle* iframeStyle = document.seamlessParentIFrame()->renderStyle(); - if (iframeStyle) - documentStyle.get().inheritFrom(iframeStyle); - } - // FIXME: It's not clear which values below we want to override in the seamless case! - documentStyle.get().setDisplay(BLOCK); - if (!seamlessWithParent) { - documentStyle.get().setRTLOrdering(document.visuallyOrdered() ? VisualOrder : LogicalOrder); - documentStyle.get().setZoom(!document.printing() ? renderView.frame().pageZoomFactor() : 1); - documentStyle.get().setPageScaleTransform(renderView.frame().frameScaleFactor()); - documentStyle.get().setLocale(document.contentLanguage()); - } + documentStyle.setDisplay(BLOCK); + documentStyle.setRTLOrdering(document.visuallyOrdered() ? VisualOrder : LogicalOrder); + documentStyle.setZoom(!document.printing() ? renderView.frame().pageZoomFactor() : 1); + documentStyle.setPageScaleTransform(renderView.frame().frameScaleFactor()); + FontCascadeDescription documentFontDescription = documentStyle.fontDescription(); + documentFontDescription.setLocale(document.contentLanguage()); + documentStyle.setFontDescription(WTFMove(documentFontDescription)); + // This overrides any -webkit-user-modify inherited from the parent iframe. - documentStyle.get().setUserModify(document.inDesignMode() ? READ_WRITE : READ_ONLY); + documentStyle.setUserModify(document.inDesignMode() ? READ_WRITE : READ_ONLY); #if PLATFORM(IOS) if (document.inDesignMode()) - documentStyle.get().setTextSizeAdjust(TextSizeAdjustment(NoTextSizeAdjustment)); + documentStyle.setTextSizeAdjust(TextSizeAdjustment(NoTextSizeAdjustment)); #endif Element* docElement = document.documentElement(); - RenderObject* docElementRenderer = docElement ? docElement->renderer() : 0; + RenderObject* docElementRenderer = docElement ? docElement->renderer() : nullptr; if (docElementRenderer) { // Use the direction and writing-mode of the body to set the // viewport's direction and writing-mode unless the property is set on the document element. // If there is no body, then use the document element. - RenderObject* bodyRenderer = document.body() ? document.body()->renderer() : 0; - if (bodyRenderer && !document.writingModeSetOnDocumentElement()) - documentStyle.get().setWritingMode(bodyRenderer->style().writingMode()); + auto* body = document.bodyOrFrameset(); + RenderObject* bodyRenderer = body ? body->renderer() : nullptr; + if (bodyRenderer && !docElementRenderer->style().hasExplicitlySetWritingMode()) + documentStyle.setWritingMode(bodyRenderer->style().writingMode()); else - documentStyle.get().setWritingMode(docElementRenderer->style().writingMode()); - if (bodyRenderer && !document.directionSetOnDocumentElement()) - documentStyle.get().setDirection(bodyRenderer->style().direction()); + documentStyle.setWritingMode(docElementRenderer->style().writingMode()); + if (bodyRenderer && !docElementRenderer->style().hasExplicitlySetDirection()) + documentStyle.setDirection(bodyRenderer->style().direction()); else - documentStyle.get().setDirection(docElementRenderer->style().direction()); + documentStyle.setDirection(docElementRenderer->style().direction()); } const Pagination& pagination = renderView.frameView().pagination(); if (pagination.mode != Pagination::Unpaginated) { - documentStyle.get().setColumnStylesFromPaginationMode(pagination.mode); - documentStyle.get().setColumnGap(pagination.gap); - if (renderView.hasColumns() || renderView.multiColumnFlowThread()) - renderView.updateColumnProgressionFromStyle(&documentStyle.get()); + documentStyle.setColumnStylesFromPaginationMode(pagination.mode); + documentStyle.setColumnGap(pagination.gap); + if (renderView.multiColumnFlowThread()) + renderView.updateColumnProgressionFromStyle(documentStyle); + if (renderView.page().paginationLineGridEnabled()) { + documentStyle.setLineGrid("-webkit-default-pagination-grid"); + documentStyle.setLineSnap(LineSnapContain); + } } - // Seamless iframes want to inherit their font from their parent iframe, so early return before setting the font. - if (seamlessWithParent) - return documentStyle; - const Settings& settings = renderView.frame().settings(); - FontDescription fontDescription; - fontDescription.setScript(localeToScriptCodeForFontSelection(documentStyle.get().locale())); - fontDescription.setUsePrinterFont(document.printing() || !settings.screenFontSubstitutionEnabled()); + FontCascadeDescription fontDescription; + fontDescription.setLocale(document.contentLanguage()); fontDescription.setRenderingMode(settings.fontRenderingMode()); - const AtomicString& standardFont = settings.standardFontFamily(fontDescription.script()); - if (!standardFont.isEmpty()) { - fontDescription.setGenericFamily(FontDescription::StandardFamily); - fontDescription.setOneFamily(standardFont); - } - fontDescription.setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1); + fontDescription.setOneFamily(standardFamily); + + fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium); int size = fontSizeForKeyword(CSSValueMedium, false, document); fontDescription.setSpecifiedSize(size); bool useSVGZoomRules = document.isSVGDocument(); - fontDescription.setComputedSize(computedFontSizeFromSpecifiedSize(size, fontDescription.isAbsoluteSize(), useSVGZoomRules, &documentStyle.get(), document)); + fontDescription.setComputedSize(computedFontSizeFromSpecifiedSize(size, fontDescription.isAbsoluteSize(), useSVGZoomRules, &documentStyle, document)); FontOrientation fontOrientation; NonCJKGlyphOrientation glyphOrientation; - documentStyle.get().getFontAndGlyphOrientation(fontOrientation, glyphOrientation); + std::tie(fontOrientation, glyphOrientation) = documentStyle.fontAndGlyphOrientation(); fontDescription.setOrientation(fontOrientation); fontDescription.setNonCJKGlyphOrientation(glyphOrientation); - documentStyle.get().setFontDescription(fontDescription); + documentStyle.setFontDescription(fontDescription); - CSSFontSelector* fontSelector = document.styleResolverIfExists() ? document.styleResolverIfExists()->fontSelector() : 0; - documentStyle.get().font().update(fontSelector); + documentStyle.fontCascade().update(&const_cast<Document&>(document).fontSelector()); return documentStyle; } diff --git a/Source/WebCore/style/StyleResolveForDocument.h b/Source/WebCore/style/StyleResolveForDocument.h index f72473b54..1569d831c 100644 --- a/Source/WebCore/style/StyleResolveForDocument.h +++ b/Source/WebCore/style/StyleResolveForDocument.h @@ -23,10 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef StyleResolveForDocument_h -#define StyleResolveForDocument_h +#pragma once -#include <wtf/PassRef.h> +#include <wtf/Ref.h> namespace WebCore { @@ -35,9 +34,7 @@ class RenderStyle; namespace Style { -PassRef<RenderStyle> resolveForDocument(const Document&); +RenderStyle resolveForDocument(const Document&); } } - -#endif diff --git a/Source/WebCore/style/StyleResolveTree.cpp b/Source/WebCore/style/StyleResolveTree.cpp deleted file mode 100644 index f0d4a8d96..000000000 --- a/Source/WebCore/style/StyleResolveTree.cpp +++ /dev/null @@ -1,915 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2001 Peter Kelly (pmk@post.com) - * (C) 2001 Dirk Mueller (mueller@kde.org) - * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. - * (C) 2007 Eric Seidel (eric@webkit.org) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "StyleResolveTree.h" - -#include "AXObjectCache.h" -#include "AnimationController.h" -#include "CSSFontSelector.h" -#include "Element.h" -#include "ElementIterator.h" -#include "ElementRareData.h" -#include "FlowThreadController.h" -#include "InsertionPoint.h" -#include "NodeRenderStyle.h" -#include "NodeRenderingTraversal.h" -#include "NodeTraversal.h" -#include "RenderElement.h" -#include "RenderFullScreen.h" -#include "RenderNamedFlowThread.h" -#include "RenderText.h" -#include "RenderView.h" -#include "RenderWidget.h" -#include "Settings.h" -#include "ShadowRoot.h" -#include "StyleResolveForDocument.h" -#include "StyleResolver.h" -#include "Text.h" - -#if PLATFORM(IOS) -#include "CSSFontSelector.h" -#include "WKContentObservation.h" -#endif - -namespace WebCore { - -namespace Style { - -enum DetachType { NormalDetach, ReattachDetach }; - -static void attachRenderTree(Element&, PassRefPtr<RenderStyle>); -static void attachTextRenderer(Text&); -static void detachRenderTree(Element&, DetachType); -static void resolveTree(Element&, Change); - -Change determineChange(const RenderStyle* s1, const RenderStyle* s2) -{ - if (!s1 || !s2) - return Detach; - if (s1->display() != s2->display()) - return Detach; - if (s1->hasPseudoStyle(FIRST_LETTER) != s2->hasPseudoStyle(FIRST_LETTER)) - return Detach; - // We just detach if a renderer acquires or loses a column-span, since spanning elements - // typically won't contain much content. - if (s1->columnSpan() != s2->columnSpan()) - return Detach; - if (!s1->contentDataEquivalent(s2)) - return Detach; - // When text-combine property has been changed, we need to prepare a separate renderer object. - // When text-combine is on, we use RenderCombineText, otherwise RenderText. - // https://bugs.webkit.org/show_bug.cgi?id=55069 - if (s1->hasTextCombine() != s2->hasTextCombine()) - return Detach; - // We need to reattach the node, so that it is moved to the correct RenderFlowThread. - if (s1->flowThread() != s2->flowThread()) - return Detach; - // When the region thread has changed, we need to prepare a separate render region object. - if (s1->regionThread() != s2->regionThread()) - return Detach; - - if (*s1 != *s2) { - if (s1->inheritedNotEqual(s2)) - return Inherit; - if (s1->hasExplicitlyInheritedProperties() || s2->hasExplicitlyInheritedProperties()) - return Inherit; - - return NoInherit; - } - // If the pseudoStyles have changed, we want any StyleChange that is not NoChange - // because setStyle will do the right thing with anything else. - if (s1->hasAnyPublicPseudoStyles()) { - for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < FIRST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) { - if (s1->hasPseudoStyle(pseudoId)) { - RenderStyle* ps2 = s2->getCachedPseudoStyle(pseudoId); - if (!ps2) - return NoInherit; - RenderStyle* ps1 = s1->getCachedPseudoStyle(pseudoId); - if (!ps1 || *ps1 != *ps2) - return NoInherit; - } - } - } - - return NoChange; -} - -static bool isRendererReparented(const RenderObject* renderer) -{ - if (!renderer->node()->isElementNode()) - return false; - if (!renderer->style().flowThread().isEmpty()) - return true; - return false; -} - -static RenderObject* nextSiblingRenderer(const Element& element, const ContainerNode* renderingParentNode) -{ - // Avoid an O(N^2) problem with this function by not checking for - // nextRenderer() when the parent element hasn't attached yet. - // FIXME: Why would we get here anyway if parent is not attached? - if (renderingParentNode && !renderingParentNode->renderer()) - return nullptr; - for (Node* sibling = NodeRenderingTraversal::nextSibling(&element); sibling; sibling = NodeRenderingTraversal::nextSibling(sibling)) { - RenderObject* renderer = sibling->renderer(); - if (renderer && !isRendererReparented(renderer)) - return renderer; - } - return nullptr; -} - -static bool shouldCreateRenderer(const Element& element, const ContainerNode* renderingParent) -{ - if (!element.document().shouldCreateRenderers()) - return false; - if (!renderingParent) - return false; - RenderObject* parentRenderer = renderingParent->renderer(); - if (!parentRenderer) - return false; - if (!parentRenderer->canHaveChildren() && !(element.isPseudoElement() && parentRenderer->canHaveGeneratedChildren())) - return false; - if (!renderingParent->childShouldCreateRenderer(element)) - return false; - return true; -} - -// Check the specific case of elements that are children of regions but are flowed into a flow thread themselves. -static bool elementInsideRegionNeedsRenderer(Element& element, const ContainerNode* renderingParentNode, RefPtr<RenderStyle>& style) -{ -#if ENABLE(CSS_REGIONS) - // The parent of a region should always be an element. - const RenderElement* parentRenderer = renderingParentNode ? renderingParentNode->renderer() : 0; - - bool parentIsRegion = parentRenderer && !parentRenderer->canHaveChildren() && parentRenderer->isRenderNamedFlowFragmentContainer(); - bool parentIsNonRenderedInsideRegion = !parentRenderer && element.parentElement() && element.parentElement()->isInsideRegion(); - if (!parentIsRegion && !parentIsNonRenderedInsideRegion) - return false; - - if (!style) - style = element.styleForRenderer(); - - // Children of this element will only be allowed to be flowed into other flow-threads if display is NOT none. - if (element.rendererIsNeeded(*style)) - element.setIsInsideRegion(true); - - if (element.shouldMoveToFlowThread(*style)) - return true; -#else - UNUSED_PARAM(element); - UNUSED_PARAM(renderingParentNode); - UNUSED_PARAM(style); -#endif - return false; -} - -#if ENABLE(CSS_REGIONS) -static RenderNamedFlowThread* moveToFlowThreadIfNeeded(Element& element, const RenderStyle& style) -{ - if (!element.shouldMoveToFlowThread(style)) - return 0; - FlowThreadController& flowThreadController = element.document().renderView()->flowThreadController(); - RenderNamedFlowThread& parentFlowRenderer = flowThreadController.ensureRenderFlowThreadWithName(style.flowThread()); - flowThreadController.registerNamedFlowContentElement(element, parentFlowRenderer); - return &parentFlowRenderer; -} -#endif - -static void createRendererIfNeeded(Element& element, PassRefPtr<RenderStyle> resolvedStyle) -{ - ASSERT(!element.renderer()); - - ContainerNode* renderingParentNode = NodeRenderingTraversal::parent(&element); - - RefPtr<RenderStyle> style = resolvedStyle; - - element.setIsInsideRegion(false); - - if (!shouldCreateRenderer(element, renderingParentNode) && !elementInsideRegionNeedsRenderer(element, renderingParentNode, style)) - return; - - if (!style) - style = element.styleForRenderer(); - - RenderNamedFlowThread* parentFlowRenderer = 0; -#if ENABLE(CSS_REGIONS) - parentFlowRenderer = moveToFlowThreadIfNeeded(element, *style); -#endif - - if (!element.rendererIsNeeded(*style)) - return; - - RenderElement* parentRenderer; - RenderObject* nextRenderer; - if (parentFlowRenderer) { - parentRenderer = parentFlowRenderer; - nextRenderer = parentFlowRenderer->nextRendererForNode(&element); - } else { - // FIXME: Make this path Element only, handle the root special case separately. - parentRenderer = renderingParentNode->renderer(); - nextRenderer = nextSiblingRenderer(element, renderingParentNode); - } - - RenderElement* newRenderer = element.createElementRenderer(style.releaseNonNull()).leakPtr(); - if (!newRenderer) - return; - if (!parentRenderer->isChildAllowed(*newRenderer, newRenderer->style())) { - newRenderer->destroy(); - return; - } - - // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style - // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail. - newRenderer->setFlowThreadState(parentRenderer->flowThreadState()); - - // Code below updateAnimations() can depend on Element::renderer() already being set. - element.setRenderer(newRenderer); - - // FIXME: There's probably a better way to factor this. - // This just does what setAnimatedStyle() does, except with setStyleInternal() instead of setStyle(). - newRenderer->setStyleInternal(newRenderer->animation().updateAnimations(*newRenderer, newRenderer->style())); - - newRenderer->initializeStyle(); - -#if ENABLE(FULLSCREEN_API) - Document& document = element.document(); - if (document.webkitIsFullScreen() && document.webkitCurrentFullScreenElement() == &element) { - newRenderer = RenderFullScreen::wrapRenderer(newRenderer, parentRenderer, document); - if (!newRenderer) - return; - } -#endif - // Note: Adding newRenderer instead of renderer(). renderer() may be a child of newRenderer. - parentRenderer->addChild(newRenderer, nextRenderer); -} - -static RenderObject* previousSiblingRenderer(const Text& textNode) -{ - if (textNode.renderer()) - return textNode.renderer()->previousSibling(); - for (Node* sibling = NodeRenderingTraversal::previousSibling(&textNode); sibling; sibling = NodeRenderingTraversal::previousSibling(sibling)) { - RenderObject* renderer = sibling->renderer(); - if (renderer && !isRendererReparented(renderer)) - return renderer; - } - return 0; -} - -static RenderObject* nextSiblingRenderer(const Text& textNode) -{ - if (textNode.renderer()) - return textNode.renderer()->nextSibling(); - for (Node* sibling = NodeRenderingTraversal::nextSibling(&textNode); sibling; sibling = NodeRenderingTraversal::nextSibling(sibling)) { - RenderObject* renderer = sibling->renderer(); - if (renderer && !isRendererReparented(renderer)) - return renderer; - } - return 0; -} - -static void reattachTextRenderersForWhitespaceOnlySiblingsAfterAttachIfNeeded(Node& current) -{ - if (isInsertionPoint(current)) - return; - // This function finds sibling text renderers where the results of textRendererIsNeeded may have changed as a result of - // the current node gaining or losing the renderer. This can only affect white space text nodes. - for (Node* sibling = NodeRenderingTraversal::nextSibling(¤t); sibling; sibling = NodeRenderingTraversal::nextSibling(sibling)) { - // Siblings haven't been attached yet. They will be handled normally when they are. - if (sibling->styleChangeType() == ReconstructRenderTree) - return; - if (sibling->isElementNode()) { - // Text renderers beyond rendered elements can't be affected. - if (!sibling->renderer() || isRendererReparented(sibling->renderer())) - continue; - return; - } - if (!sibling->isTextNode()) - continue; - Text& textSibling = *toText(sibling); - if (!textSibling.length() || !textSibling.containsOnlyWhitespace()) - return; - Text& whitespaceTextSibling = textSibling; - bool hadRenderer = whitespaceTextSibling.renderer(); - detachTextRenderer(whitespaceTextSibling); - attachTextRenderer(whitespaceTextSibling); - // No changes, futher renderers can't be affected. - if (hadRenderer == !!whitespaceTextSibling.renderer()) - return; - } -} - -static bool textRendererIsNeeded(const Text& textNode, const RenderObject& parentRenderer, const RenderStyle& style) -{ - if (textNode.isEditingText()) - return true; - if (!textNode.length()) - return false; - if (style.display() == NONE) - return false; - if (!textNode.containsOnlyWhitespace()) - return true; - // This text node has nothing but white space. We may still need a renderer in some cases. - if (parentRenderer.isTable() || parentRenderer.isTableRow() || parentRenderer.isTableSection() || parentRenderer.isRenderTableCol() || parentRenderer.isFrameSet()) - return false; - if (style.preserveNewline()) // pre/pre-wrap/pre-line always make renderers. - return true; - - RenderObject* previousRenderer = previousSiblingRenderer(textNode); - if (previousRenderer && previousRenderer->isBR()) // <span><br/> <br/></span> - return false; - - if (parentRenderer.isRenderInline()) { - // <span><div/> <div/></span> - if (previousRenderer && !previousRenderer->isInline()) - return false; - } else { - if (parentRenderer.isRenderBlock() && !parentRenderer.childrenInline() && (!previousRenderer || !previousRenderer->isInline())) - return false; - - RenderObject* first = toRenderElement(parentRenderer).firstChild(); - while (first && first->isFloatingOrOutOfFlowPositioned()) - first = first->nextSibling(); - RenderObject* nextRenderer = nextSiblingRenderer(textNode); - if (!first || nextRenderer == first) { - // Whitespace at the start of a block just goes away. Don't even make a render object for this text. - return false; - } - } - return true; -} - -static void createTextRendererIfNeeded(Text& textNode) -{ - ASSERT(!textNode.renderer()); - - ContainerNode* renderingParentNode = NodeRenderingTraversal::parent(&textNode); - if (!renderingParentNode) - return; - RenderElement* parentRenderer = renderingParentNode->renderer(); - if (!parentRenderer || !parentRenderer->canHaveChildren()) - return; - if (!renderingParentNode->childShouldCreateRenderer(textNode)) - return; - - const auto& style = parentRenderer->style(); - - if (!textRendererIsNeeded(textNode, *parentRenderer, style)) - return; - - auto newRenderer = textNode.createTextRenderer(style); - ASSERT(newRenderer); - - if (!parentRenderer->isChildAllowed(*newRenderer, style)) - return; - - // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style - // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail. - newRenderer->setFlowThreadState(parentRenderer->flowThreadState()); - - RenderObject* nextRenderer = nextSiblingRenderer(textNode); - textNode.setRenderer(newRenderer.get()); - // Parent takes care of the animations, no need to call setAnimatableStyle. - parentRenderer->addChild(newRenderer.leakPtr(), nextRenderer); -} - -void attachTextRenderer(Text& textNode) -{ - createTextRendererIfNeeded(textNode); - - textNode.clearNeedsStyleRecalc(); -} - -void detachTextRenderer(Text& textNode) -{ - if (textNode.renderer()) - textNode.renderer()->destroyAndCleanupAnonymousWrappers(); - textNode.setRenderer(0); -} - -void updateTextRendererAfterContentChange(Text& textNode, unsigned offsetOfReplacedData, unsigned lengthOfReplacedData) -{ - RenderText* textRenderer = textNode.renderer(); - if (!textRenderer) { - attachTextRenderer(textNode); - reattachTextRenderersForWhitespaceOnlySiblingsAfterAttachIfNeeded(textNode); - return; - } - RenderObject* parentRenderer = NodeRenderingTraversal::parent(&textNode)->renderer(); - if (!textRendererIsNeeded(textNode, *parentRenderer, textRenderer->style())) { - detachTextRenderer(textNode); - attachTextRenderer(textNode); - reattachTextRenderersForWhitespaceOnlySiblingsAfterAttachIfNeeded(textNode); - return; - } - textRenderer->setTextWithOffset(textNode.dataImpl(), offsetOfReplacedData, lengthOfReplacedData); -} - -static void attachDistributedChildren(InsertionPoint& insertionPoint) -{ - if (ShadowRoot* shadowRoot = insertionPoint.containingShadowRoot()) - ContentDistributor::ensureDistribution(shadowRoot); - for (Node* current = insertionPoint.firstDistributed(); current; current = insertionPoint.nextDistributedTo(current)) { - if (current->isTextNode()) { - if (current->renderer()) - continue; - attachTextRenderer(*toText(current)); - continue; - } - if (current->isElementNode()) { - if (current->renderer()) - detachRenderTree(*toElement(current)); - attachRenderTree(*toElement(current), nullptr); - } - } -} - -static void attachChildren(ContainerNode& current) -{ - if (isInsertionPoint(current)) - attachDistributedChildren(toInsertionPoint(current)); - - for (Node* child = current.firstChild(); child; child = child->nextSibling()) { - ASSERT(!child->renderer() || current.shadowRoot() || isInsertionPoint(current)); - if (child->renderer()) - continue; - if (child->isTextNode()) { - attachTextRenderer(*toText(child)); - continue; - } - if (child->isElementNode()) - attachRenderTree(*toElement(child), nullptr); - } -} - -static void attachShadowRoot(ShadowRoot& shadowRoot) -{ - StyleResolver& styleResolver = shadowRoot.document().ensureStyleResolver(); - styleResolver.pushParentShadowRoot(&shadowRoot); - - attachChildren(shadowRoot); - - styleResolver.popParentShadowRoot(&shadowRoot); - - shadowRoot.clearNeedsStyleRecalc(); - shadowRoot.clearChildNeedsStyleRecalc(); -} - -static PseudoElement* beforeOrAfterPseudoElement(Element& current, PseudoId pseudoId) -{ - ASSERT(pseudoId == BEFORE || pseudoId == AFTER); - if (pseudoId == BEFORE) - return current.beforePseudoElement(); - return current.afterPseudoElement(); -} - -static void setBeforeOrAfterPseudoElement(Element& current, PassRefPtr<PseudoElement> pseudoElement, PseudoId pseudoId) -{ - ASSERT(pseudoId == BEFORE || pseudoId == AFTER); - if (pseudoId == BEFORE) { - current.setBeforePseudoElement(pseudoElement); - return; - } - current.setAfterPseudoElement(pseudoElement); -} - -static void clearBeforeOrAfterPseudoElement(Element& current, PseudoId pseudoId) -{ - ASSERT(pseudoId == BEFORE || pseudoId == AFTER); - if (pseudoId == BEFORE) { - current.clearBeforePseudoElement(); - return; - } - current.clearAfterPseudoElement(); -} - -static bool needsPseudeElement(Element& current, PseudoId pseudoId) -{ - if (!current.document().styleSheetCollection().usesBeforeAfterRules()) - return false; - if (!current.renderer() || !current.renderer()->canHaveGeneratedChildren()) - return false; - if (current.isPseudoElement()) - return false; - if (!pseudoElementRendererIsNeeded(current.renderer()->getCachedPseudoStyle(pseudoId))) - return false; - return true; -} - -static void attachBeforeOrAfterPseudoElementIfNeeded(Element& current, PseudoId pseudoId) -{ - if (!needsPseudeElement(current, pseudoId)) - return; - RefPtr<PseudoElement> pseudoElement = PseudoElement::create(current, pseudoId); - setBeforeOrAfterPseudoElement(current, pseudoElement, pseudoId); - attachRenderTree(*pseudoElement, nullptr); -} - -static void attachRenderTree(Element& current, PassRefPtr<RenderStyle> resolvedStyle) -{ - PostAttachCallbackDisabler callbackDisabler(current.document()); - WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; - - if (current.hasCustomStyleResolveCallbacks()) - current.willAttachRenderers(); - - createRendererIfNeeded(current, resolvedStyle); - - if (current.parentElement() && current.parentElement()->isInCanvasSubtree()) - current.setIsInCanvasSubtree(true); - - attachBeforeOrAfterPseudoElementIfNeeded(current, BEFORE); - - StyleResolverParentPusher parentPusher(¤t); - - // When a shadow root exists, it does the work of attaching the children. - if (ShadowRoot* shadowRoot = current.shadowRoot()) { - parentPusher.push(); - attachShadowRoot(*shadowRoot); - } else if (current.firstChild()) - parentPusher.push(); - - attachChildren(current); - - current.clearNeedsStyleRecalc(); - current.clearChildNeedsStyleRecalc(); - - if (AXObjectCache* cache = current.document().axObjectCache()) - cache->updateCacheAfterNodeIsAttached(¤t); - - attachBeforeOrAfterPseudoElementIfNeeded(current, AFTER); - - current.updateFocusAppearanceAfterAttachIfNeeded(); - - if (current.hasCustomStyleResolveCallbacks()) - current.didAttachRenderers(); -} - -static void detachDistributedChildren(InsertionPoint& insertionPoint) -{ - for (Node* current = insertionPoint.firstDistributed(); current; current = insertionPoint.nextDistributedTo(current)) { - if (current->isTextNode()) { - detachTextRenderer(*toText(current)); - continue; - } - if (current->isElementNode()) - detachRenderTree(*toElement(current)); - } -} - -static void detachChildren(ContainerNode& current, DetachType detachType) -{ - if (isInsertionPoint(current)) - detachDistributedChildren(toInsertionPoint(current)); - - for (Node* child = current.firstChild(); child; child = child->nextSibling()) { - if (child->isTextNode()) { - Style::detachTextRenderer(*toText(child)); - continue; - } - if (child->isElementNode()) - detachRenderTree(*toElement(child), detachType); - } - current.clearChildNeedsStyleRecalc(); -} - -static void detachShadowRoot(ShadowRoot& shadowRoot, DetachType detachType) -{ - detachChildren(shadowRoot, detachType); -} - -static void detachRenderTree(Element& current, DetachType detachType) -{ - WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; - - if (current.hasCustomStyleResolveCallbacks()) - current.willDetachRenderers(); - - current.clearStyleDerivedDataBeforeDetachingRenderer(); - - // Do not remove the element's hovered and active status - // if performing a reattach. - if (detachType != ReattachDetach) - current.clearHoverAndActiveStatusBeforeDetachingRenderer(); - - if (ShadowRoot* shadowRoot = current.shadowRoot()) - detachShadowRoot(*shadowRoot, detachType); - - detachChildren(current, detachType); - - if (current.renderer()) - current.renderer()->destroyAndCleanupAnonymousWrappers(); - current.setRenderer(0); - - if (current.hasCustomStyleResolveCallbacks()) - current.didDetachRenderers(); -} - -static bool pseudoStyleCacheIsInvalid(RenderElement* renderer, RenderStyle* newStyle) -{ - const RenderStyle& currentStyle = renderer->style(); - - const PseudoStyleCache* pseudoStyleCache = currentStyle.cachedPseudoStyles(); - if (!pseudoStyleCache) - return false; - - size_t cacheSize = pseudoStyleCache->size(); - for (size_t i = 0; i < cacheSize; ++i) { - RefPtr<RenderStyle> newPseudoStyle; - PseudoId pseudoId = pseudoStyleCache->at(i)->styleType(); - if (pseudoId == FIRST_LINE || pseudoId == FIRST_LINE_INHERITED) - newPseudoStyle = renderer->uncachedFirstLineStyle(newStyle); - else - newPseudoStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId), newStyle, newStyle); - if (!newPseudoStyle) - return true; - if (*newPseudoStyle != *pseudoStyleCache->at(i)) { - if (pseudoId < FIRST_INTERNAL_PSEUDOID) - newStyle->setHasPseudoStyle(pseudoId); - newStyle->addCachedPseudoStyle(newPseudoStyle); - if (pseudoId == FIRST_LINE || pseudoId == FIRST_LINE_INHERITED) { - // FIXME: We should do an actual diff to determine whether a repaint vs. layout - // is needed, but for now just assume a layout will be required. The diff code - // in RenderObject::setStyle would need to be factored out so that it could be reused. - renderer->setNeedsLayoutAndPrefWidthsRecalc(); - } - return true; - } - } - return false; -} - -static Change resolveLocal(Element& current, Change inheritedChange) -{ - Change localChange = Detach; - RefPtr<RenderStyle> newStyle; - RefPtr<RenderStyle> currentStyle = current.renderStyle(); - - Document& document = current.document(); - if (currentStyle && current.styleChangeType() != ReconstructRenderTree) { - newStyle = current.styleForRenderer(); - localChange = determineChange(currentStyle.get(), newStyle.get()); - } - if (localChange == Detach) { - if (current.renderer() || current.inNamedFlow()) - detachRenderTree(current, ReattachDetach); - attachRenderTree(current, newStyle.release()); - reattachTextRenderersForWhitespaceOnlySiblingsAfterAttachIfNeeded(current); - - return Detach; - } - - if (RenderElement* renderer = current.renderer()) { - if (localChange != NoChange || pseudoStyleCacheIsInvalid(renderer, newStyle.get()) || (inheritedChange == Force && renderer->requiresForcedStyleRecalcPropagation()) || current.styleChangeType() == SyntheticStyleChange) - renderer->setAnimatableStyle(*newStyle); - else if (current.needsStyleRecalc()) { - // Although no change occurred, we use the new style so that the cousin style sharing code won't get - // fooled into believing this style is the same. - renderer->setStyleInternal(*newStyle); - } - } - - // If "rem" units are used anywhere in the document, and if the document element's font size changes, then go ahead and force font updating - // all the way down the tree. This is simpler than having to maintain a cache of objects (and such font size changes should be rare anyway). - if (document.styleSheetCollection().usesRemUnits() && document.documentElement() == ¤t && localChange != NoChange && currentStyle && newStyle && currentStyle->fontSize() != newStyle->fontSize()) { - // Cached RenderStyles may depend on the re units. - if (StyleResolver* styleResolver = document.styleResolverIfExists()) - styleResolver->invalidateMatchedPropertiesCache(); - return Force; - } - if (inheritedChange == Force) - return Force; - if (current.styleChangeType() >= FullStyleChange) - return Force; - - return localChange; -} - -static void updateTextStyle(Text& text) -{ - RenderText* renderer = text.renderer(); - - if (!text.needsStyleRecalc()) - return; - if (renderer) - renderer->setText(text.dataImpl()); - else { - attachTextRenderer(text); - reattachTextRenderersForWhitespaceOnlySiblingsAfterAttachIfNeeded(text); - } - text.clearNeedsStyleRecalc(); -} - -static void resolveShadowTree(ShadowRoot* shadowRoot, Style::Change change) -{ - if (!shadowRoot) - return; - StyleResolver& styleResolver = shadowRoot->document().ensureStyleResolver(); - styleResolver.pushParentShadowRoot(shadowRoot); - - for (Node* child = shadowRoot->firstChild(); child; child = child->nextSibling()) { - if (child->isTextNode()) { - // Current user agent ShadowRoots don't have immediate text children so this branch is never actually taken. - updateTextStyle(*toText(child)); - continue; - } - resolveTree(*toElement(child), change); - } - - styleResolver.popParentShadowRoot(shadowRoot); - shadowRoot->clearNeedsStyleRecalc(); - shadowRoot->clearChildNeedsStyleRecalc(); -} - -static void updateBeforeOrAfterPseudoElement(Element& current, Change change, PseudoId pseudoId) -{ - if (PseudoElement* existingPseudoElement = beforeOrAfterPseudoElement(current, pseudoId)) { - if (needsPseudeElement(current, pseudoId)) - resolveTree(*existingPseudoElement, current.needsStyleRecalc() ? Force : change); - else - clearBeforeOrAfterPseudoElement(current, pseudoId); - return; - } - attachBeforeOrAfterPseudoElementIfNeeded(current, pseudoId); -} - -#if PLATFORM(IOS) -static EVisibility elementImplicitVisibility(const Element* element) -{ - RenderObject* renderer = element->renderer(); - if (!renderer) - return VISIBLE; - - RenderStyle& style = renderer->style(); - - Length width(style.width()); - Length height(style.height()); - if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0)) - return HIDDEN; - - Length top(style.top()); - Length left(style.left()); - if (left.isFixed() && width.isFixed() && -left.value() >= width.value()) - return HIDDEN; - - if (top.isFixed() && height.isFixed() && -top.value() >= height.value()) - return HIDDEN; - return VISIBLE; -} - -class CheckForVisibilityChangeOnRecalcStyle { -public: - CheckForVisibilityChangeOnRecalcStyle(Element* element, RenderStyle* currentStyle) - : m_element(element) - , m_previousDisplay(currentStyle ? currentStyle->display() : NONE) - , m_previousVisibility(currentStyle ? currentStyle->visibility() : HIDDEN) - , m_previousImplicitVisibility(WKObservingContentChanges() && WKContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE) - { - } - ~CheckForVisibilityChangeOnRecalcStyle() - { - if (!WKObservingContentChanges()) - return; - RenderStyle* style = m_element->renderStyle(); - if (!style) - return; - if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN) - || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element.get()) == VISIBLE)) - WKSetObservedContentChange(WKContentVisibilityChange); - } -private: - RefPtr<Element> m_element; - EDisplay m_previousDisplay; - EVisibility m_previousVisibility; - EVisibility m_previousImplicitVisibility; -}; -#endif // PLATFORM(IOS) - -void resolveTree(Element& current, Change change) -{ - ASSERT(change != Detach); - - if (current.hasCustomStyleResolveCallbacks()) { - if (!current.willRecalcStyle(change)) - return; - } - - ContainerNode* renderingParentNode = NodeRenderingTraversal::parent(¤t); - bool hasParentStyle = renderingParentNode && renderingParentNode->renderStyle(); - bool hasDirectAdjacentRules = current.childrenAffectedByDirectAdjacentRules(); - bool hasIndirectAdjacentRules = current.childrenAffectedByForwardPositionalRules(); - -#if PLATFORM(IOS) - CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(¤t, current.renderStyle()); -#endif - - if (change > NoChange || current.needsStyleRecalc()) - current.resetComputedStyle(); - - if (hasParentStyle && (change >= Inherit || current.needsStyleRecalc())) - change = resolveLocal(current, change); - - if (change != Detach) { - StyleResolverParentPusher parentPusher(¤t); - - if (ShadowRoot* shadowRoot = current.shadowRoot()) { - if (change >= Inherit || shadowRoot->childNeedsStyleRecalc() || shadowRoot->needsStyleRecalc()) { - parentPusher.push(); - resolveShadowTree(shadowRoot, change); - } - } - - updateBeforeOrAfterPseudoElement(current, change, BEFORE); - - // FIXME: This check is good enough for :hover + foo, but it is not good enough for :hover + foo + bar. - // For now we will just worry about the common case, since it's a lot trickier to get the second case right - // without doing way too much re-resolution. - bool forceCheckOfNextElementSibling = false; - bool forceCheckOfAnyElementSibling = false; - for (Node* child = current.firstChild(); child; child = child->nextSibling()) { - if (child->isTextNode()) { - updateTextStyle(*toText(child)); - continue; - } - if (!child->isElementNode()) - continue; - Element* childElement = toElement(child); - bool childRulesChanged = childElement->needsStyleRecalc() && childElement->styleChangeType() == FullStyleChange; - if ((forceCheckOfNextElementSibling || forceCheckOfAnyElementSibling)) - childElement->setNeedsStyleRecalc(); - if (change >= Inherit || childElement->childNeedsStyleRecalc() || childElement->needsStyleRecalc()) { - parentPusher.push(); - resolveTree(*childElement, change); - } - forceCheckOfNextElementSibling = childRulesChanged && hasDirectAdjacentRules; - forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules); - } - - updateBeforeOrAfterPseudoElement(current, change, AFTER); - } - - current.clearNeedsStyleRecalc(); - current.clearChildNeedsStyleRecalc(); - - if (current.hasCustomStyleResolveCallbacks()) - current.didRecalcStyle(change); -} - -void resolveTree(Document& document, Change change) -{ - bool resolveRootStyle = change == Force || (document.shouldDisplaySeamlesslyWithParent() && change >= Inherit); - if (resolveRootStyle) { - auto documentStyle = resolveForDocument(document); - - // Inserting the pictograph font at the end of the font fallback list is done by the - // font selector, so set a font selector if needed. - if (Settings* settings = document.settings()) { - StyleResolver* styleResolver = document.styleResolverIfExists(); - if (settings->fontFallbackPrefersPictographs() && styleResolver) - documentStyle.get().font().update(styleResolver->fontSelector()); - } - - Style::Change documentChange = determineChange(&documentStyle.get(), &document.renderView()->style()); - if (documentChange != NoChange) - document.renderView()->setStyle(std::move(documentStyle)); - else - documentStyle.dropRef(); - } - - Element* documentElement = document.documentElement(); - if (!documentElement) - return; - if (change < Inherit && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc()) - return; - resolveTree(*documentElement, change); -} - -void detachRenderTree(Element& element) -{ - detachRenderTree(element, NormalDetach); -} - -} -} diff --git a/Source/WebCore/style/StyleScope.cpp b/Source/WebCore/style/StyleScope.cpp new file mode 100644 index 000000000..7f53c3dc1 --- /dev/null +++ b/Source/WebCore/style/StyleScope.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2004-2009, 2011-2012, 2015-2016 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved. + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) Research In Motion Limited 2010-2011. 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 + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "StyleScope.h" + +#include "CSSStyleSheet.h" +#include "Element.h" +#include "ElementChildIterator.h" +#include "ExtensionStyleSheets.h" +#include "HTMLIFrameElement.h" +#include "HTMLLinkElement.h" +#include "HTMLSlotElement.h" +#include "HTMLStyleElement.h" +#include "InspectorInstrumentation.h" +#include "ProcessingInstruction.h" +#include "SVGStyleElement.h" +#include "Settings.h" +#include "ShadowRoot.h" +#include "StyleInvalidationAnalysis.h" +#include "StyleResolver.h" +#include "StyleSheetContents.h" +#include "StyleSheetList.h" +#include "UserContentController.h" +#include "UserContentURLPattern.h" +#include "UserStyleSheet.h" +#include <wtf/SetForScope.h> + +namespace WebCore { + +using namespace ContentExtensions; +using namespace HTMLNames; + +namespace Style { + +Scope::Scope(Document& document) + : m_document(document) + , m_pendingUpdateTimer(*this, &Scope::pendingUpdateTimerFired) +{ +} + +Scope::Scope(ShadowRoot& shadowRoot) + : m_document(shadowRoot.documentScope()) + , m_shadowRoot(&shadowRoot) + , m_pendingUpdateTimer(*this, &Scope::pendingUpdateTimerFired) +{ +} + +Scope::~Scope() +{ +} + +bool Scope::shouldUseSharedUserAgentShadowTreeStyleResolver() const +{ + if (!m_shadowRoot) + return false; + if (m_shadowRoot->mode() != ShadowRootMode::UserAgent) + return false; + // If we have stylesheets in the user agent shadow tree use per-scope resolver. + if (!m_styleSheetCandidateNodes.isEmpty()) + return false; + return true; +} + +StyleResolver& Scope::resolver() +{ + if (shouldUseSharedUserAgentShadowTreeStyleResolver()) + return m_document.userAgentShadowTreeStyleResolver(); + + if (!m_resolver) { + SetForScope<bool> isUpdatingStyleResolver { m_isUpdatingStyleResolver, true }; + m_resolver = std::make_unique<StyleResolver>(m_document); + m_resolver->appendAuthorStyleSheets(m_activeStyleSheets); + } + ASSERT(!m_shadowRoot || &m_document == &m_shadowRoot->document()); + ASSERT(&m_resolver->document() == &m_document); + return *m_resolver; +} + +StyleResolver* Scope::resolverIfExists() +{ + if (shouldUseSharedUserAgentShadowTreeStyleResolver()) + return &m_document.userAgentShadowTreeStyleResolver(); + + return m_resolver.get(); +} + +void Scope::clearResolver() +{ + m_resolver = nullptr; + + if (!m_shadowRoot) + m_document.didClearStyleResolver(); +} + +Scope& Scope::forNode(Node& node) +{ + ASSERT(node.isConnected()); + auto* shadowRoot = node.containingShadowRoot(); + if (shadowRoot) + return shadowRoot->styleScope(); + return node.document().styleScope(); +} + +Scope* Scope::forOrdinal(Element& element, ScopeOrdinal ordinal) +{ + switch (ordinal) { + case ScopeOrdinal::Element: + return &forNode(element); + case ScopeOrdinal::ContainingHost: { + auto* containingShadowRoot = element.containingShadowRoot(); + if (!containingShadowRoot) + return nullptr; + return &forNode(*containingShadowRoot->host()); + } + case ScopeOrdinal::Shadow: { + auto* shadowRoot = element.shadowRoot(); + if (!shadowRoot) + return nullptr; + return &shadowRoot->styleScope(); + } + default: { + ASSERT(ordinal >= ScopeOrdinal::FirstSlot); + auto slotIndex = ScopeOrdinal::FirstSlot; + for (auto* slot = element.assignedSlot(); slot; slot = slot->assignedSlot(), ++slotIndex) { + if (slotIndex == ordinal) + return &forNode(*slot); + } + return nullptr; + } + } +} + +void Scope::setPreferredStylesheetSetName(const String& name) +{ + if (m_preferredStylesheetSetName == name) + return; + m_preferredStylesheetSetName = name; + didChangeActiveStyleSheetCandidates(); +} + +void Scope::setSelectedStylesheetSetName(const String& name) +{ + if (m_selectedStylesheetSetName == name) + return; + m_selectedStylesheetSetName = name; + didChangeActiveStyleSheetCandidates(); +} + +// This method is called whenever a top-level stylesheet has finished loading. +void Scope::removePendingSheet() +{ + // Make sure we knew this sheet was pending, and that our count isn't out of sync. + ASSERT(m_pendingStyleSheetCount > 0); + + m_pendingStyleSheetCount--; + if (m_pendingStyleSheetCount) + return; + + didChangeActiveStyleSheetCandidates(); + + if (!m_shadowRoot) + m_document.didRemoveAllPendingStylesheet(); +} + +void Scope::addStyleSheetCandidateNode(Node& node, bool createdByParser) +{ + if (!node.isConnected()) + return; + + // Until the <body> exists, we have no choice but to compare document positions, + // since styles outside of the body and head continue to be shunted into the head + // (and thus can shift to end up before dynamically added DOM content that is also + // outside the body). + if ((createdByParser && m_document.bodyOrFrameset()) || m_styleSheetCandidateNodes.isEmpty()) { + m_styleSheetCandidateNodes.add(&node); + return; + } + + // Determine an appropriate insertion point. + auto begin = m_styleSheetCandidateNodes.begin(); + auto end = m_styleSheetCandidateNodes.end(); + auto it = end; + Node* followingNode = nullptr; + do { + --it; + Node* n = *it; + unsigned short position = n->compareDocumentPosition(node); + if (position == Node::DOCUMENT_POSITION_FOLLOWING) { + m_styleSheetCandidateNodes.insertBefore(followingNode, &node); + return; + } + followingNode = n; + } while (it != begin); + + m_styleSheetCandidateNodes.insertBefore(followingNode, &node); +} + +void Scope::removeStyleSheetCandidateNode(Node& node) +{ + if (m_styleSheetCandidateNodes.remove(&node)) + didChangeActiveStyleSheetCandidates(); +} + +void Scope::collectActiveStyleSheets(Vector<RefPtr<StyleSheet>>& sheets) +{ + if (!m_document.settings().authorAndUserStylesEnabled()) + return; + + for (auto& node : m_styleSheetCandidateNodes) { + StyleSheet* sheet = nullptr; + if (is<ProcessingInstruction>(*node)) { + // Processing instruction (XML documents only). + // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion. + ProcessingInstruction& pi = downcast<ProcessingInstruction>(*node); + sheet = pi.sheet(); +#if ENABLE(XSLT) + // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806> + if (pi.isXSL() && !m_document.transformSourceDocument()) { + // Don't apply XSL transforms until loading is finished. + if (!m_document.parsing()) + m_document.applyXSLTransform(&pi); + return; + } +#endif + } else if (is<HTMLLinkElement>(*node) || is<HTMLStyleElement>(*node) || is<SVGStyleElement>(*node)) { + Element& element = downcast<Element>(*node); + AtomicString title = element.attributeWithoutSynchronization(titleAttr); + bool enabledViaScript = false; + if (is<HTMLLinkElement>(element)) { + // <LINK> element + HTMLLinkElement& linkElement = downcast<HTMLLinkElement>(element); + if (linkElement.isDisabled()) + continue; + enabledViaScript = linkElement.isEnabledViaScript(); + if (linkElement.styleSheetIsLoading()) { + // it is loading but we should still decide which style sheet set to use + if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSetName.isEmpty()) { + if (!linkElement.attributeWithoutSynchronization(relAttr).contains("alternate")) { + m_preferredStylesheetSetName = title; + m_selectedStylesheetSetName = title; + } + } + continue; + } + if (!linkElement.sheet()) + title = nullAtom; + } + // Get the current preferred styleset. This is the + // set of sheets that will be enabled. + if (is<SVGStyleElement>(element)) + sheet = downcast<SVGStyleElement>(element).sheet(); + else if (is<HTMLLinkElement>(element)) + sheet = downcast<HTMLLinkElement>(element).sheet(); + else + sheet = downcast<HTMLStyleElement>(element).sheet(); + // Check to see if this sheet belongs to a styleset + // (thus making it PREFERRED or ALTERNATE rather than + // PERSISTENT). + auto& rel = element.attributeWithoutSynchronization(relAttr); + if (!enabledViaScript && !title.isEmpty()) { + // Yes, we have a title. + if (m_preferredStylesheetSetName.isEmpty()) { + // No preferred set has been established. If + // we are NOT an alternate sheet, then establish + // us as the preferred set. Otherwise, just ignore + // this sheet. + if (is<HTMLStyleElement>(element) || !rel.contains("alternate")) + m_preferredStylesheetSetName = m_selectedStylesheetSetName = title; + } + if (title != m_preferredStylesheetSetName) + sheet = nullptr; + } + + if (rel.contains("alternate") && title.isEmpty()) + sheet = nullptr; + } + if (sheet) + sheets.append(sheet); + } +} + +Scope::StyleResolverUpdateType Scope::analyzeStyleSheetChange(const Vector<RefPtr<CSSStyleSheet>>& newStylesheets, bool& requiresFullStyleRecalc) +{ + requiresFullStyleRecalc = true; + + unsigned newStylesheetCount = newStylesheets.size(); + + if (!resolverIfExists()) + return Reconstruct; + + auto& styleResolver = *resolverIfExists(); + + // Find out which stylesheets are new. + unsigned oldStylesheetCount = m_activeStyleSheets.size(); + if (newStylesheetCount < oldStylesheetCount) + return Reconstruct; + + Vector<StyleSheetContents*> addedSheets; + unsigned newIndex = 0; + for (unsigned oldIndex = 0; oldIndex < oldStylesheetCount; ++oldIndex) { + if (newIndex >= newStylesheetCount) + return Reconstruct; + while (m_activeStyleSheets[oldIndex] != newStylesheets[newIndex]) { + addedSheets.append(&newStylesheets[newIndex]->contents()); + ++newIndex; + if (newIndex == newStylesheetCount) + return Reconstruct; + } + ++newIndex; + } + bool hasInsertions = !addedSheets.isEmpty(); + while (newIndex < newStylesheetCount) { + addedSheets.append(&newStylesheets[newIndex]->contents()); + ++newIndex; + } + // If all new sheets were added at the end of the list we can just add them to existing StyleResolver. + // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly. + auto styleResolverUpdateType = hasInsertions ? Reset : Additive; + + // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs. + if (!m_document.bodyOrFrameset() || m_document.hasNodesWithPlaceholderStyle()) + return styleResolverUpdateType; + + StyleInvalidationAnalysis invalidationAnalysis(addedSheets, styleResolver.mediaQueryEvaluator()); + if (invalidationAnalysis.dirtiesAllStyle()) + return styleResolverUpdateType; + + if (m_shadowRoot) + invalidationAnalysis.invalidateStyle(*m_shadowRoot); + else + invalidationAnalysis.invalidateStyle(m_document); + + requiresFullStyleRecalc = false; + + return styleResolverUpdateType; +} + +static void filterEnabledNonemptyCSSStyleSheets(Vector<RefPtr<CSSStyleSheet>>& result, const Vector<RefPtr<StyleSheet>>& sheets) +{ + for (auto& sheet : sheets) { + if (!is<CSSStyleSheet>(*sheet)) + continue; + CSSStyleSheet& styleSheet = downcast<CSSStyleSheet>(*sheet); + if (styleSheet.isLoading()) + continue; + if (styleSheet.disabled()) + continue; + if (!styleSheet.length()) + continue; + result.append(&styleSheet); + } +} + +void Scope::updateActiveStyleSheets(UpdateType updateType) +{ + ASSERT(!m_pendingUpdate); + + if (!m_document.hasLivingRenderTree()) + return; + + if (m_document.inStyleRecalc() || m_document.inRenderTreeUpdate()) { + // Protect against deleting style resolver in the middle of a style resolution. + // Crash stacks indicate we can get here when a resource load fails synchronously (for example due to content blocking). + // FIXME: These kind of cases should be eliminated and this path replaced by an assert. + m_pendingUpdate = UpdateType::ContentsOrInterpretation; + m_document.scheduleForcedStyleRecalc(); + return; + } + + Vector<RefPtr<StyleSheet>> activeStyleSheets; + collectActiveStyleSheets(activeStyleSheets); + + Vector<RefPtr<CSSStyleSheet>> activeCSSStyleSheets; + activeCSSStyleSheets.appendVector(m_document.extensionStyleSheets().injectedAuthorStyleSheets()); + activeCSSStyleSheets.appendVector(m_document.extensionStyleSheets().authorStyleSheetsForTesting()); + filterEnabledNonemptyCSSStyleSheets(activeCSSStyleSheets, activeStyleSheets); + + bool requiresFullStyleRecalc = true; + StyleResolverUpdateType styleResolverUpdateType = Reconstruct; + if (updateType == UpdateType::ActiveSet) + styleResolverUpdateType = analyzeStyleSheetChange(activeCSSStyleSheets, requiresFullStyleRecalc); + + updateStyleResolver(activeCSSStyleSheets, styleResolverUpdateType); + + m_weakCopyOfActiveStyleSheetListForFastLookup = nullptr; + m_activeStyleSheets.swap(activeCSSStyleSheets); + m_styleSheetsForStyleSheetList.swap(activeStyleSheets); + + InspectorInstrumentation::activeStyleSheetsUpdated(m_document); + + for (const auto& sheet : m_activeStyleSheets) { + if (sheet->contents().usesStyleBasedEditability()) + m_usesStyleBasedEditability = true; + } + + // FIXME: Move this code somewhere else. + if (requiresFullStyleRecalc) { + if (m_shadowRoot) { + for (auto& shadowChild : childrenOfType<Element>(*m_shadowRoot)) + shadowChild.invalidateStyleForSubtree(); + if (m_shadowRoot->host()) { + if (!resolver().ruleSets().authorStyle().hostPseudoClassRules().isEmpty()) + m_shadowRoot->host()->invalidateStyle(); + if (!resolver().ruleSets().authorStyle().slottedPseudoElementRules().isEmpty()) { + for (auto& shadowChild : childrenOfType<Element>(*m_shadowRoot->host())) + shadowChild.invalidateStyle(); + } + } + } else + m_document.scheduleForcedStyleRecalc(); + } +} + +void Scope::updateStyleResolver(Vector<RefPtr<CSSStyleSheet>>& activeStyleSheets, StyleResolverUpdateType updateType) +{ + if (updateType == Reconstruct) { + clearResolver(); + return; + } + auto& styleResolver = resolver(); + + SetForScope<bool> isUpdatingStyleResolver { m_isUpdatingStyleResolver, true }; + if (updateType == Reset) { + styleResolver.ruleSets().resetAuthorStyle(); + styleResolver.appendAuthorStyleSheets(activeStyleSheets); + } else { + ASSERT(updateType == Additive); + unsigned firstNewIndex = m_activeStyleSheets.size(); + Vector<RefPtr<CSSStyleSheet>> newStyleSheets; + newStyleSheets.appendRange(activeStyleSheets.begin() + firstNewIndex, activeStyleSheets.end()); + styleResolver.appendAuthorStyleSheets(newStyleSheets); + } +} + +const Vector<RefPtr<CSSStyleSheet>> Scope::activeStyleSheetsForInspector() +{ + Vector<RefPtr<CSSStyleSheet>> result; + + result.appendVector(m_document.extensionStyleSheets().injectedAuthorStyleSheets()); + result.appendVector(m_document.extensionStyleSheets().authorStyleSheetsForTesting()); + + for (auto& styleSheet : m_styleSheetsForStyleSheetList) { + if (!is<CSSStyleSheet>(*styleSheet)) + continue; + + CSSStyleSheet& sheet = downcast<CSSStyleSheet>(*styleSheet); + if (sheet.disabled()) + continue; + + result.append(&sheet); + } + + return result; +} + +bool Scope::activeStyleSheetsContains(const CSSStyleSheet* sheet) const +{ + if (!m_weakCopyOfActiveStyleSheetListForFastLookup) { + m_weakCopyOfActiveStyleSheetListForFastLookup = std::make_unique<HashSet<const CSSStyleSheet*>>(); + for (auto& activeStyleSheet : m_activeStyleSheets) + m_weakCopyOfActiveStyleSheetListForFastLookup->add(activeStyleSheet.get()); + } + return m_weakCopyOfActiveStyleSheetListForFastLookup->contains(sheet); +} + +void Scope::flushPendingSelfUpdate() +{ + ASSERT(m_pendingUpdate); + + auto updateType = *m_pendingUpdate; + + clearPendingUpdate(); + updateActiveStyleSheets(updateType); +} + +void Scope::flushPendingDescendantUpdates() +{ + ASSERT(m_hasDescendantWithPendingUpdate); + ASSERT(!m_shadowRoot); + for (auto* descendantShadowRoot : m_document.inDocumentShadowRoots()) + descendantShadowRoot->styleScope().flushPendingUpdate(); + m_hasDescendantWithPendingUpdate = false; +} + +void Scope::clearPendingUpdate() +{ + m_pendingUpdateTimer.stop(); + m_pendingUpdate = { }; +} + +void Scope::scheduleUpdate(UpdateType update) +{ + // FIXME: The m_isUpdatingStyleResolver test is here because extension stylesheets can get us here from StyleResolver::appendAuthorStyleSheets. + if (update == UpdateType::ContentsOrInterpretation && !m_isUpdatingStyleResolver) + clearResolver(); + + if (!m_pendingUpdate || *m_pendingUpdate < update) { + m_pendingUpdate = update; + if (m_shadowRoot) + m_document.styleScope().m_hasDescendantWithPendingUpdate = true; + } + + if (m_pendingUpdateTimer.isActive()) + return; + m_pendingUpdateTimer.startOneShot(0); +} + +void Scope::didChangeActiveStyleSheetCandidates() +{ + scheduleUpdate(UpdateType::ActiveSet); +} + +void Scope::didChangeStyleSheetContents() +{ + scheduleUpdate(UpdateType::ContentsOrInterpretation); +} + +void Scope::didChangeStyleSheetEnvironment() +{ + if (!m_shadowRoot) { + for (auto* descendantShadowRoot : m_document.inDocumentShadowRoots()) { + // Stylesheets is author shadow roots are are potentially affected. + if (descendantShadowRoot->mode() != ShadowRootMode::UserAgent) + descendantShadowRoot->styleScope().scheduleUpdate(UpdateType::ContentsOrInterpretation); + } + } + scheduleUpdate(UpdateType::ContentsOrInterpretation); +} + +void Scope::pendingUpdateTimerFired() +{ + flushPendingUpdate(); +} + +const Vector<RefPtr<StyleSheet>>& Scope::styleSheetsForStyleSheetList() +{ + // FIXME: StyleSheetList content should be updated separately from style resolver updates. + flushPendingUpdate(); + return m_styleSheetsForStyleSheetList; +} + +} +} diff --git a/Source/WebCore/style/StyleScope.h b/Source/WebCore/style/StyleScope.h new file mode 100644 index 000000000..cecec2b47 --- /dev/null +++ b/Source/WebCore/style/StyleScope.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2004-2010, 2012-2013, 2015-2016 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2011 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 + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#pragma once + +#include "Timer.h" +#include <memory> +#include <wtf/FastMalloc.h> +#include <wtf/HashMap.h> +#include <wtf/ListHashSet.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class CSSStyleSheet; +class Document; +class Element; +class Node; +class StyleResolver; +class StyleSheet; +class StyleSheetContents; +class StyleSheetList; +class ShadowRoot; +class TreeScope; + +namespace Style { + +// This is used to identify style scopes that can affect an element. +// Scopes are in tree-of-trees order. Styles from earlier scopes win over later ones (modulo !important). +enum class ScopeOrdinal : int { + ContainingHost = -1, // Author-exposed UA pseudo classes from the host tree scope. + Element = 0, // Normal rules in the same tree where the element is. + FirstSlot = 1, // ::slotted rules in the parent's shadow tree. Values greater than FirstSlot indicate subsequent slots in the chain. + Shadow = std::numeric_limits<int>::max(), // :host rules in element's own shadow tree. +}; + +class Scope { + WTF_MAKE_FAST_ALLOCATED; +public: + explicit Scope(Document&); + explicit Scope(ShadowRoot&); + + ~Scope(); + + const Vector<RefPtr<CSSStyleSheet>>& activeStyleSheets() const { return m_activeStyleSheets; } + + const Vector<RefPtr<StyleSheet>>& styleSheetsForStyleSheetList(); + const Vector<RefPtr<CSSStyleSheet>> activeStyleSheetsForInspector(); + + void addStyleSheetCandidateNode(Node&, bool createdByParser); + void removeStyleSheetCandidateNode(Node&); + + String preferredStylesheetSetName() const { return m_preferredStylesheetSetName; } + String selectedStylesheetSetName() const { return m_selectedStylesheetSetName; } + void setPreferredStylesheetSetName(const String&); + void setSelectedStylesheetSetName(const String&); + + void addPendingSheet() { m_pendingStyleSheetCount++; } + void removePendingSheet(); + + bool hasPendingSheets() const { return m_pendingStyleSheetCount > 0; } + + bool usesStyleBasedEditability() { return m_usesStyleBasedEditability; } + + bool activeStyleSheetsContains(const CSSStyleSheet*) const; + + // This is called when some stylesheet becomes newly enabled or disabled. + void didChangeActiveStyleSheetCandidates(); + // This is called when contents of a stylesheet is mutated. + void didChangeStyleSheetContents(); + // This is called when the environment where we intrepret the stylesheets changes (for example switching to printing). + // The change is assumed to potentially affect all author and user stylesheets including shadow roots. + WEBCORE_EXPORT void didChangeStyleSheetEnvironment(); + + bool hasPendingUpdate() const { return m_pendingUpdate || m_hasDescendantWithPendingUpdate; } + void flushPendingUpdate(); + + StyleResolver& resolver(); + StyleResolver* resolverIfExists(); + void clearResolver(); + + const Document& document() const { return m_document; } + + static Scope& forNode(Node&); + static Scope* forOrdinal(Element&, ScopeOrdinal); + +private: + bool shouldUseSharedUserAgentShadowTreeStyleResolver() const; + + enum class UpdateType { ActiveSet, ContentsOrInterpretation }; + void updateActiveStyleSheets(UpdateType); + void scheduleUpdate(UpdateType); + + WEBCORE_EXPORT void flushPendingSelfUpdate(); + WEBCORE_EXPORT void flushPendingDescendantUpdates(); + + void collectActiveStyleSheets(Vector<RefPtr<StyleSheet>>&); + + enum StyleResolverUpdateType { + Reconstruct, + Reset, + Additive + }; + StyleResolverUpdateType analyzeStyleSheetChange(const Vector<RefPtr<CSSStyleSheet>>& newStylesheets, bool& requiresFullStyleRecalc); + void updateStyleResolver(Vector<RefPtr<CSSStyleSheet>>&, StyleResolverUpdateType); + + void pendingUpdateTimerFired(); + void clearPendingUpdate(); + + Document& m_document; + ShadowRoot* m_shadowRoot { nullptr }; + + std::unique_ptr<StyleResolver> m_resolver; + + Vector<RefPtr<StyleSheet>> m_styleSheetsForStyleSheetList; + Vector<RefPtr<CSSStyleSheet>> m_activeStyleSheets; + + Timer m_pendingUpdateTimer; + + mutable std::unique_ptr<HashSet<const CSSStyleSheet*>> m_weakCopyOfActiveStyleSheetListForFastLookup; + + // Track the number of currently loading top-level stylesheets needed for rendering. + // Sheets loaded using the @import directive are not included in this count. + // We use this count of pending sheets to detect when we can begin attaching + // elements and when it is safe to execute scripts. + int m_pendingStyleSheetCount { 0 }; + + std::optional<UpdateType> m_pendingUpdate; + bool m_hasDescendantWithPendingUpdate { false }; + + ListHashSet<Node*> m_styleSheetCandidateNodes; + + String m_preferredStylesheetSetName; + String m_selectedStylesheetSetName; + + bool m_usesStyleBasedEditability { false }; + bool m_isUpdatingStyleResolver { false }; +}; + +inline void Scope::flushPendingUpdate() +{ + if (m_hasDescendantWithPendingUpdate) + flushPendingDescendantUpdates(); + if (m_pendingUpdate) + flushPendingSelfUpdate(); +} + +inline ScopeOrdinal& operator++(ScopeOrdinal& ordinal) +{ + ASSERT(ordinal < ScopeOrdinal::Shadow); + return ordinal = static_cast<ScopeOrdinal>(static_cast<int>(ordinal) + 1); +} + +} +} diff --git a/Source/WebCore/style/StyleSharingResolver.cpp b/Source/WebCore/style/StyleSharingResolver.cpp new file mode 100644 index 000000000..b7a28496a --- /dev/null +++ b/Source/WebCore/style/StyleSharingResolver.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StyleSharingResolver.h" + +#include "DocumentRuleSets.h" +#include "ElementRuleCollector.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "NodeRenderStyle.h" +#include "RenderStyle.h" +#include "SVGElement.h" +#include "ShadowRoot.h" +#include "StyleScope.h" +#include "StyleUpdate.h" +#include "StyledElement.h" +#include "VisitedLinkState.h" +#include "WebVTTElement.h" +#include "XMLNames.h" + +namespace WebCore { +namespace Style { + +static const unsigned cStyleSearchThreshold = 10; + +struct SharingResolver::Context { + const Update& update; + const StyledElement& element; + bool elementAffectedByClassRules; + EInsideLink elementLinkState; +}; + +SharingResolver::SharingResolver(const Document& document, const DocumentRuleSets& ruleSets, const SelectorFilter& selectorFilter) + : m_document(document) + , m_ruleSets(ruleSets) + , m_selectorFilter(selectorFilter) +{ +} + +static inline bool parentElementPreventsSharing(const Element& parentElement) +{ + return parentElement.hasFlagsSetDuringStylingOfChildren(); +} + +static inline bool elementHasDirectionAuto(const Element& element) +{ + // FIXME: This line is surprisingly hot, we may wish to inline hasDirectionAuto into StyleResolver. + return is<HTMLElement>(element) && downcast<HTMLElement>(element).hasDirectionAuto(); +} + +std::unique_ptr<RenderStyle> SharingResolver::resolve(const Element& searchElement, const Update& update) +{ + if (!is<StyledElement>(searchElement)) + return nullptr; + auto& element = downcast<StyledElement>(searchElement); + if (!element.parentElement()) + return nullptr; + auto& parentElement = *element.parentElement(); + if (parentElement.shadowRoot()) + return nullptr; + if (!update.elementStyle(parentElement)) + return nullptr; + // If the element has inline style it is probably unique. + if (element.inlineStyle()) + return nullptr; + if (element.isSVGElement() && downcast<SVGElement>(element).animatedSMILStyleProperties()) + return nullptr; + // Ids stop style sharing if they show up in the stylesheets. + auto& id = element.idForStyleResolution(); + if (!id.isNull() && m_ruleSets.features().idsInRules.contains(id)) + return nullptr; + if (parentElementPreventsSharing(parentElement)) + return nullptr; + if (&element == m_document.cssTarget()) + return nullptr; + if (elementHasDirectionAuto(element)) + return nullptr; + if (element.shadowRoot() && !element.shadowRoot()->styleScope().resolver().ruleSets().authorStyle().hostPseudoClassRules().isEmpty()) + return nullptr; + + Context context { + update, + element, + element.hasClass() && classNamesAffectedByRules(element.classNames()), + m_document.visitedLinkState().determineLinkState(element) + }; + + // Check previous siblings and their cousins. + unsigned count = 0; + StyledElement* shareElement = nullptr; + Node* cousinList = element.previousSibling(); + while (cousinList) { + shareElement = findSibling(context, cousinList, count); + if (shareElement) + break; + if (count >= cStyleSearchThreshold) + break; + cousinList = locateCousinList(cousinList->parentElement()); + } + + // If we have exhausted all our budget or our cousins. + if (!shareElement) + return nullptr; + + // Can't share if sibling rules apply. This is checked at the end as it should rarely fail. + if (styleSharingCandidateMatchesRuleSet(element, m_ruleSets.sibling())) + return nullptr; + // Can't share if attribute rules apply. + if (styleSharingCandidateMatchesRuleSet(element, m_ruleSets.uncommonAttribute())) + return nullptr; + // Tracking child index requires unique style for each node. This may get set by the sibling rule match above. + if (parentElementPreventsSharing(parentElement)) + return nullptr; + + m_elementsSharingStyle.add(&element, shareElement); + + return RenderStyle::clonePtr(*update.elementStyle(*shareElement)); +} + +StyledElement* SharingResolver::findSibling(const Context& context, Node* node, unsigned& count) const +{ + for (; node; node = node->previousSibling()) { + if (!is<StyledElement>(*node)) + continue; + if (canShareStyleWithElement(context, downcast<StyledElement>(*node))) + break; + if (count++ >= cStyleSearchThreshold) + return nullptr; + } + return downcast<StyledElement>(node); +} + +Node* SharingResolver::locateCousinList(const Element* parent) const +{ + for (unsigned count = 0; count < cStyleSearchThreshold; ++count) { + auto* elementSharingParentStyle = m_elementsSharingStyle.get(parent); + if (!elementSharingParentStyle) + return nullptr; + if (!parentElementPreventsSharing(*elementSharingParentStyle)) { + if (auto* cousin = elementSharingParentStyle->lastChild()) + return cousin; + } + parent = elementSharingParentStyle; + } + + return nullptr; +} + +static bool canShareStyleWithControl(const HTMLFormControlElement& element, const HTMLFormControlElement& formElement) +{ + if (!is<HTMLInputElement>(formElement) || !is<HTMLInputElement>(element)) + return false; + + auto& thisInputElement = downcast<HTMLInputElement>(formElement); + auto& otherInputElement = downcast<HTMLInputElement>(element); + + if (thisInputElement.isAutoFilled() != otherInputElement.isAutoFilled()) + return false; + if (thisInputElement.shouldAppearChecked() != otherInputElement.shouldAppearChecked()) + return false; + if (thisInputElement.isRequired() != otherInputElement.isRequired()) + return false; + + if (formElement.isDisabledFormControl() != element.isDisabledFormControl()) + return false; + + if (formElement.isInRange() != element.isInRange()) + return false; + + if (formElement.isOutOfRange() != element.isOutOfRange()) + return false; + + return true; +} + +bool SharingResolver::canShareStyleWithElement(const Context& context, const StyledElement& candidateElement) const +{ + auto& element = context.element; + auto* style = context.update.elementStyle(candidateElement); + if (!style) + return false; + if (style->unique()) + return false; + if (style->hasUniquePseudoStyle()) + return false; + if (candidateElement.tagQName() != element.tagQName()) + return false; + if (candidateElement.inlineStyle()) + return false; + if (candidateElement.needsStyleRecalc()) + return false; + if (candidateElement.isSVGElement() && downcast<SVGElement>(candidateElement).animatedSMILStyleProperties()) + return false; + if (candidateElement.isLink() != element.isLink()) + return false; + if (candidateElement.hovered() != element.hovered()) + return false; + if (candidateElement.active() != element.active()) + return false; + if (candidateElement.focused() != element.focused()) + return false; + if (candidateElement.shadowPseudoId() != element.shadowPseudoId()) + return false; + if (&candidateElement == m_document.cssTarget()) + return false; + if (!sharingCandidateHasIdenticalStyleAffectingAttributes(context, candidateElement)) + return false; + if (const_cast<StyledElement&>(candidateElement).additionalPresentationAttributeStyle() != const_cast<StyledElement&>(element).additionalPresentationAttributeStyle()) + return false; + if (candidateElement.affectsNextSiblingElementStyle() || candidateElement.styleIsAffectedByPreviousSibling()) + return false; + if (candidateElement.styleAffectedByFocusWithin() || element.styleAffectedByFocusWithin()) + return false; + + auto& candidateElementId = candidateElement.idForStyleResolution(); + if (!candidateElementId.isNull() && m_ruleSets.features().idsInRules.contains(candidateElementId)) + return false; + + bool isControl = is<HTMLFormControlElement>(candidateElement); + + if (isControl != is<HTMLFormControlElement>(element)) + return false; + + if (isControl && !canShareStyleWithControl(downcast<HTMLFormControlElement>(element), downcast<HTMLFormControlElement>(candidateElement))) + return false; + + if (style->transitions() || style->animations()) + return false; + + // Turn off style sharing for elements that can gain layers for reasons outside of the style system. + // See comments in RenderObject::setStyle(). + if (candidateElement.hasTagName(HTMLNames::iframeTag) || candidateElement.hasTagName(HTMLNames::frameTag)) + return false; + + if (candidateElement.hasTagName(HTMLNames::embedTag) || candidateElement.hasTagName(HTMLNames::objectTag) || candidateElement.hasTagName(HTMLNames::appletTag) || candidateElement.hasTagName(HTMLNames::canvasTag)) + return false; + + if (elementHasDirectionAuto(candidateElement)) + return false; + + if (candidateElement.isLink() && context.elementLinkState != style->insideLink()) + return false; + + if (candidateElement.elementData() != element.elementData()) { + if (candidateElement.attributeWithoutSynchronization(HTMLNames::readonlyAttr) != element.attributeWithoutSynchronization(HTMLNames::readonlyAttr)) + return false; + if (candidateElement.isSVGElement()) { + if (candidateElement.getAttribute(HTMLNames::typeAttr) != element.getAttribute(HTMLNames::typeAttr)) + return false; + } else { + if (candidateElement.attributeWithoutSynchronization(HTMLNames::typeAttr) != element.attributeWithoutSynchronization(HTMLNames::typeAttr)) + return false; + } + } + + if (candidateElement.matchesValidPseudoClass() != element.matchesValidPseudoClass()) + return false; + + if (element.matchesInvalidPseudoClass() != element.matchesValidPseudoClass()) + return false; + + if (candidateElement.matchesIndeterminatePseudoClass() != element.matchesIndeterminatePseudoClass()) + return false; + + if (candidateElement.matchesDefaultPseudoClass() != element.matchesDefaultPseudoClass()) + return false; + + if (candidateElement.shadowRoot() && !candidateElement.shadowRoot()->styleScope().resolver().ruleSets().authorStyle().hostPseudoClassRules().isEmpty()) + return false; + +#if ENABLE(FULLSCREEN_API) + if (&element == m_document.webkitCurrentFullScreenElement() || &element == m_document.webkitCurrentFullScreenElement()) + return false; +#endif + return true; +} + +bool SharingResolver::styleSharingCandidateMatchesRuleSet(const StyledElement& element, const RuleSet* ruleSet) const +{ + if (!ruleSet) + return false; + + ElementRuleCollector collector(const_cast<StyledElement&>(element), m_ruleSets, &m_selectorFilter); + return collector.hasAnyMatchingRules(ruleSet); +} + +bool SharingResolver::sharingCandidateHasIdenticalStyleAffectingAttributes(const Context& context, const StyledElement& sharingCandidate) const +{ + auto& element = context.element; + if (element.elementData() == sharingCandidate.elementData()) + return true; + if (element.attributeWithoutSynchronization(XMLNames::langAttr) != sharingCandidate.attributeWithoutSynchronization(XMLNames::langAttr)) + return false; + if (element.attributeWithoutSynchronization(HTMLNames::langAttr) != sharingCandidate.attributeWithoutSynchronization(HTMLNames::langAttr)) + return false; + + if (context.elementAffectedByClassRules) { + if (!sharingCandidate.hasClass()) + return false; + // SVG elements require a (slow!) getAttribute comparision because "class" is an animatable attribute for SVG. + if (element.isSVGElement()) { + if (element.getAttribute(HTMLNames::classAttr) != sharingCandidate.getAttribute(HTMLNames::classAttr)) + return false; + } else { + if (element.classNames() != sharingCandidate.classNames()) + return false; + } + } else if (sharingCandidate.hasClass() && classNamesAffectedByRules(sharingCandidate.classNames())) + return false; + + if (const_cast<StyledElement&>(element).presentationAttributeStyle() != const_cast<StyledElement&>(sharingCandidate).presentationAttributeStyle()) + return false; + + return true; +} + +bool SharingResolver::classNamesAffectedByRules(const SpaceSplitString& classNames) const +{ + for (unsigned i = 0; i < classNames.size(); ++i) { + if (m_ruleSets.features().classesInRules.contains(classNames[i])) + return true; + } + return false; +} + + +} +} diff --git a/Source/WebCore/style/StyleSharingResolver.h b/Source/WebCore/style/StyleSharingResolver.h new file mode 100644 index 000000000..e33e37250 --- /dev/null +++ b/Source/WebCore/style/StyleSharingResolver.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <wtf/HashMap.h> + +namespace WebCore { + +class Document; +class DocumentRuleSets; +class Element; +class Node; +class RenderStyle; +class RuleSet; +class SelectorFilter; +class SpaceSplitString; +class StyledElement; + +namespace Style { + +class Update; + +class SharingResolver { +public: + SharingResolver(const Document&, const DocumentRuleSets&, const SelectorFilter&); + + std::unique_ptr<RenderStyle> resolve(const Element&, const Update&); + +private: + struct Context; + + StyledElement* findSibling(const Context&, Node*, unsigned& count) const; + Node* locateCousinList(const Element* parent) const; + bool canShareStyleWithElement(const Context&, const StyledElement& candidateElement) const; + bool styleSharingCandidateMatchesRuleSet(const StyledElement&, const RuleSet*) const; + bool sharingCandidateHasIdenticalStyleAffectingAttributes(const Context&, const StyledElement& sharingCandidate) const; + bool classNamesAffectedByRules(const SpaceSplitString& classNames) const; + + const Document& m_document; + const DocumentRuleSets& m_ruleSets; + const SelectorFilter& m_selectorFilter; + + HashMap<const Element*, const Element*> m_elementsSharingStyle; +}; + +} +} diff --git a/Source/WebCore/style/StyleTreeResolver.cpp b/Source/WebCore/style/StyleTreeResolver.cpp new file mode 100644 index 000000000..7cf382da0 --- /dev/null +++ b/Source/WebCore/style/StyleTreeResolver.cpp @@ -0,0 +1,560 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2007 David Smith (catfish.man@gmail.com) + * Copyright (C) 2004-2010, 2012-2016 Apple Inc. All rights reserved. + * (C) 2007 Eric Seidel (eric@webkit.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "StyleTreeResolver.h" + +#include "CSSFontSelector.h" +#include "ComposedTreeAncestorIterator.h" +#include "ComposedTreeIterator.h" +#include "ElementIterator.h" +#include "HTMLBodyElement.h" +#include "HTMLMeterElement.h" +#include "HTMLNames.h" +#include "HTMLProgressElement.h" +#include "HTMLSlotElement.h" +#include "LoaderStrategy.h" +#include "MainFrame.h" +#include "NodeRenderStyle.h" +#include "Page.h" +#include "PlatformStrategies.h" +#include "Settings.h" +#include "ShadowRoot.h" +#include "StyleFontSizeFunctions.h" +#include "StyleResolver.h" +#include "StyleScope.h" +#include "Text.h" + +namespace WebCore { + +namespace Style { + +static std::unique_ptr<RenderStyle> makePlaceholderStyle(Document& document) +{ + auto placeholderStyle = RenderStyle::createPtr(); + placeholderStyle->setDisplay(NONE); + placeholderStyle->setIsPlaceholderStyle(); + + FontCascadeDescription fontDescription; + fontDescription.setOneFamily(standardFamily); + fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium); + float size = Style::fontSizeForKeyword(CSSValueMedium, false, document); + fontDescription.setSpecifiedSize(size); + fontDescription.setComputedSize(size); + placeholderStyle->setFontDescription(fontDescription); + + placeholderStyle->fontCascade().update(&document.fontSelector()); + return placeholderStyle; +} + +TreeResolver::TreeResolver(Document& document) + : m_document(document) +{ +} + +TreeResolver::~TreeResolver() +{ +} + +TreeResolver::Scope::Scope(Document& document) + : styleResolver(document.styleScope().resolver()) + , sharingResolver(document, styleResolver.ruleSets(), selectorFilter) +{ +} + +TreeResolver::Scope::Scope(ShadowRoot& shadowRoot, Scope& enclosingScope) + : styleResolver(shadowRoot.styleScope().resolver()) + , sharingResolver(shadowRoot.documentScope(), styleResolver.ruleSets(), selectorFilter) + , shadowRoot(&shadowRoot) + , enclosingScope(&enclosingScope) +{ +} + +TreeResolver::Parent::Parent(Document& document, Change change) + : element(nullptr) + , style(*document.renderStyle()) + , change(change) +{ +} + +TreeResolver::Parent::Parent(Element& element, const RenderStyle& style, Change change) + : element(&element) + , style(style) + , change(change) +{ +} + +void TreeResolver::pushScope(ShadowRoot& shadowRoot) +{ + m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope()))); + scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get()); +} + +void TreeResolver::pushEnclosingScope() +{ + ASSERT(scope().enclosingScope); + m_scopeStack.append(*scope().enclosingScope); + scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get()); +} + +void TreeResolver::popScope() +{ + scope().styleResolver.setOverrideDocumentElementStyle(nullptr); + return m_scopeStack.removeLast(); +} + +std::unique_ptr<RenderStyle> TreeResolver::styleForElement(Element& element, const RenderStyle& inheritedStyle) +{ + if (!m_document.haveStylesheetsLoaded() && !element.renderer()) { + m_document.setHasNodesWithPlaceholderStyle(); + return makePlaceholderStyle(m_document); + } + + if (element.hasCustomStyleResolveCallbacks()) { + RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr; + if (auto customStyle = element.resolveCustomStyle(inheritedStyle, shadowHostStyle)) { + if (customStyle->relations) + commitRelations(WTFMove(customStyle->relations), *m_update); + + return WTFMove(customStyle->renderStyle); + } + } + + if (auto style = scope().sharingResolver.resolve(element, *m_update)) + return style; + + auto elementStyle = scope().styleResolver.styleForElement(element, &inheritedStyle, parentBoxStyle(), MatchAllRules, nullptr, &scope().selectorFilter); + + if (elementStyle.relations) + commitRelations(WTFMove(elementStyle.relations), *m_update); + + return WTFMove(elementStyle.renderStyle); +} + +static void resetStyleForNonRenderedDescendants(Element& current) +{ + // FIXME: This is not correct with shadow trees. This should be done with ComposedTreeIterator. + bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle = false; + for (auto& child : childrenOfType<Element>(current)) { + bool affectedByPreviousSibling = child.styleIsAffectedByPreviousSibling() && elementNeedingStyleRecalcAffectsNextSiblingElementStyle; + if (child.needsStyleRecalc() || elementNeedingStyleRecalcAffectsNextSiblingElementStyle) + elementNeedingStyleRecalcAffectsNextSiblingElementStyle = child.affectsNextSiblingElementStyle(); + + if (child.needsStyleRecalc() || affectedByPreviousSibling) { + child.resetComputedStyle(); + child.resetStyleRelations(); + child.setHasValidStyle(); + } + + if (child.childNeedsStyleRecalc()) { + resetStyleForNonRenderedDescendants(child); + child.clearChildNeedsStyleRecalc(); + } + } +} + +static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle) +{ + if (element.renderer()) + return true; + if (newStyle.display() != NONE) + return true; + if (element.rendererIsNeeded(newStyle)) + return true; +#if ENABLE(CSS_REGIONS) + if (element.shouldMoveToFlowThread(newStyle)) + return true; +#endif + return false; +} + +ElementUpdate TreeResolver::resolveElement(Element& element) +{ + auto newStyle = styleForElement(element, parent().style); + + if (!affectsRenderedSubtree(element, *newStyle)) + return { }; + + auto update = createAnimatedElementUpdate(WTFMove(newStyle), element, parent().change); + + auto* existingStyle = element.renderStyle(); + + if (&element == m_document.documentElement()) { + m_documentElementStyle = RenderStyle::clonePtr(*update.style); + scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get()); + + if (update.change != NoChange && existingStyle && existingStyle->fontSize() != update.style->fontSize()) { + // "rem" units are relative to the document element's font size so we need to recompute everything. + // In practice this is rare. + scope().styleResolver.invalidateMatchedPropertiesCache(); + update.change = std::max(update.change, Force); + } + } + + // This is needed for resolving color:-webkit-text for subsequent elements. + // FIXME: We shouldn't mutate document when resolving style. + if (&element == m_document.body()) + m_document.setTextColor(update.style->visitedDependentColor(CSSPropertyColor)); + + // FIXME: These elements should not change renderer based on appearance property. + if (element.hasTagName(HTMLNames::meterTag) || is<HTMLProgressElement>(element)) { + if (existingStyle && update.style->appearance() != existingStyle->appearance()) + update.change = Detach; + } + + return update; +} + +const RenderStyle* TreeResolver::parentBoxStyle() const +{ + // 'display: contents' doesn't generate boxes. + for (unsigned i = m_parentStack.size(); i; --i) { + auto& parent = m_parentStack[i - 1]; + if (parent.style.display() == NONE) + return nullptr; + if (parent.style.display() != CONTENTS) + return &parent.style; + } + ASSERT_NOT_REACHED(); + return nullptr; +} + +ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderStyle> newStyle, Element& element, Change parentChange) +{ + auto validity = element.styleValidity(); + bool recompositeLayer = element.styleResolutionShouldRecompositeLayer(); + + auto makeUpdate = [&] (std::unique_ptr<RenderStyle> style, Change change) { + if (validity >= Validity::SubtreeInvalid) + change = std::max(change, validity == Validity::SubtreeAndRenderersInvalid ? Detach : Force); + if (parentChange >= Force) + change = std::max(change, parentChange); + return ElementUpdate { WTFMove(style), change, recompositeLayer }; + }; + + auto* renderer = element.renderer(); + + bool shouldReconstruct = validity >= Validity::SubtreeAndRenderersInvalid || parentChange == Detach; + if (shouldReconstruct) + return makeUpdate(WTFMove(newStyle), Detach); + + if (!renderer) { + auto keepsDisplayContents = newStyle->display() == CONTENTS && element.hasDisplayContents(); + // Some inherited property might have changed. + return makeUpdate(WTFMove(newStyle), keepsDisplayContents ? Inherit : Detach); + } + + std::unique_ptr<RenderStyle> animatedStyle; + if (element.document().frame()->animation().updateAnimations(*renderer, *newStyle, animatedStyle)) + recompositeLayer = true; + + if (animatedStyle) { + auto change = determineChange(renderer->style(), *animatedStyle); + if (renderer->hasInitialAnimatedStyle()) { + renderer->setHasInitialAnimatedStyle(false); + // When we initialize a newly created renderer with initial animated style we don't inherit it to descendants. + // The first animation frame needs to correct this. + // FIXME: We should compute animated style correctly during initial style resolution when we don't have renderers yet. + // https://bugs.webkit.org/show_bug.cgi?id=171926 + change = std::max(change, Inherit); + } + // If animation forces render tree reconstruction pass the original style. The animation will be applied on renderer construction. + // FIXME: We should always use the animated style here. + auto style = change == Detach ? WTFMove(newStyle) : WTFMove(animatedStyle); + return makeUpdate(WTFMove(style), change); + } + + auto change = determineChange(renderer->style(), *newStyle); + return makeUpdate(WTFMove(newStyle), change); +} + +void TreeResolver::pushParent(Element& element, const RenderStyle& style, Change change) +{ + scope().selectorFilter.pushParent(&element); + + Parent parent(element, style, change); + + if (auto* shadowRoot = element.shadowRoot()) { + pushScope(*shadowRoot); + parent.didPushScope = true; + } + else if (is<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(element).assignedNodes()) { + pushEnclosingScope(); + parent.didPushScope = true; + } + + m_parentStack.append(WTFMove(parent)); +} + +void TreeResolver::popParent() +{ + auto& parentElement = *parent().element; + + parentElement.setHasValidStyle(); + parentElement.clearChildNeedsStyleRecalc(); + + if (parent().didPushScope) + popScope(); + + scope().selectorFilter.popParent(); + + m_parentStack.removeLast(); +} + +void TreeResolver::popParentsToDepth(unsigned depth) +{ + ASSERT(depth); + ASSERT(m_parentStack.size() >= depth); + + while (m_parentStack.size() > depth) + popParent(); +} + +static bool shouldResolvePseudoElement(const PseudoElement* pseudoElement) +{ + if (!pseudoElement) + return false; + return pseudoElement->needsStyleRecalc(); +} + +static bool shouldResolveElement(const Element& element, Style::Change parentChange) +{ + if (parentChange >= Inherit) + return true; + if (parentChange == NoInherit) { + auto* existingStyle = element.renderStyle(); + if (existingStyle && existingStyle->hasExplicitlyInheritedProperties()) + return true; + } + if (element.needsStyleRecalc()) + return true; + if (element.hasDisplayContents()) + return true; + if (shouldResolvePseudoElement(element.beforePseudoElement())) + return true; + if (shouldResolvePseudoElement(element.afterPseudoElement())) + return true; + + return false; +} + +static void clearNeedsStyleResolution(Element& element) +{ + element.setHasValidStyle(); + if (auto* before = element.beforePseudoElement()) + before->setHasValidStyle(); + if (auto* after = element.afterPseudoElement()) + after->setHasValidStyle(); +} + +void TreeResolver::resolveComposedTree() +{ + ASSERT(m_parentStack.size() == 1); + ASSERT(m_scopeStack.size() == 1); + + auto descendants = composedTreeDescendants(m_document); + auto it = descendants.begin(); + auto end = descendants.end(); + + // FIXME: SVG <use> element may cause tree mutations during style recalc. + it.dropAssertions(); + + while (it != end) { + popParentsToDepth(it.depth()); + + auto& node = *it; + auto& parent = this->parent(); + + ASSERT(node.isConnected()); + ASSERT(node.containingShadowRoot() == scope().shadowRoot); + ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot()); + + if (is<Text>(node)) { + auto& text = downcast<Text>(node); + if (text.styleValidity() >= Validity::SubtreeAndRenderersInvalid && parent.change != Detach) + m_update->addText(text, parent.element); + + text.setHasValidStyle(); + it.traverseNextSkippingChildren(); + continue; + } + + auto& element = downcast<Element>(node); + + if (it.depth() > Settings::defaultMaximumRenderTreeDepth) { + resetStyleForNonRenderedDescendants(element); + element.clearChildNeedsStyleRecalc(); + it.traverseNextSkippingChildren(); + continue; + } + + // FIXME: We should deal with this during style invalidation. + bool affectedByPreviousSibling = element.styleIsAffectedByPreviousSibling() && parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle; + if (element.needsStyleRecalc() || parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle) + parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle = element.affectsNextSiblingElementStyle(); + + auto* style = element.renderStyle(); + auto change = NoChange; + + bool shouldResolve = shouldResolveElement(element, parent.change) || affectedByPreviousSibling; + if (shouldResolve) { + element.resetComputedStyle(); + element.resetStyleRelations(); + + if (element.hasCustomStyleResolveCallbacks()) + element.willRecalcStyle(parent.change); + + auto elementUpdate = resolveElement(element); + + if (element.hasCustomStyleResolveCallbacks()) + element.didRecalcStyle(elementUpdate.change); + + style = elementUpdate.style.get(); + change = elementUpdate.change; + + if (affectedByPreviousSibling && change != Detach) + change = Force; + + if (elementUpdate.style) + m_update->addElement(element, parent.element, WTFMove(elementUpdate)); + + clearNeedsStyleResolution(element); + } + + if (!style) { + resetStyleForNonRenderedDescendants(element); + element.clearChildNeedsStyleRecalc(); + } + + bool shouldIterateChildren = style && (element.childNeedsStyleRecalc() || change != NoChange); + if (!shouldIterateChildren) { + it.traverseNextSkippingChildren(); + continue; + } + + pushParent(element, *style, change); + + it.traverseNext(); + } + + popParentsToDepth(1); +} + +std::unique_ptr<Update> TreeResolver::resolve(Change change) +{ + auto& renderView = *m_document.renderView(); + + Element* documentElement = m_document.documentElement(); + if (!documentElement) { + m_document.styleScope().resolver(); + return nullptr; + } + if (change != Force && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc()) + return nullptr; + + m_update = std::make_unique<Update>(m_document); + m_scopeStack.append(adoptRef(*new Scope(m_document))); + m_parentStack.append(Parent(m_document, change)); + + // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc. + renderView.setUsesFirstLineRules(renderView.usesFirstLineRules() || scope().styleResolver.usesFirstLineRules()); + renderView.setUsesFirstLetterRules(renderView.usesFirstLetterRules() || scope().styleResolver.usesFirstLetterRules()); + + resolveComposedTree(); + + renderView.setUsesFirstLineRules(scope().styleResolver.usesFirstLineRules()); + renderView.setUsesFirstLetterRules(scope().styleResolver.usesFirstLetterRules()); + + ASSERT(m_scopeStack.size() == 1); + ASSERT(m_parentStack.size() == 1); + m_parentStack.clear(); + popScope(); + + if (m_update->roots().isEmpty()) + return { }; + + return WTFMove(m_update); +} + +static Vector<Function<void ()>>& postResolutionCallbackQueue() +{ + static NeverDestroyed<Vector<Function<void ()>>> vector; + return vector; +} + +void queuePostResolutionCallback(Function<void ()>&& callback) +{ + postResolutionCallbackQueue().append(WTFMove(callback)); +} + +static void suspendMemoryCacheClientCalls(Document& document) +{ + Page* page = document.page(); + if (!page || !page->areMemoryCacheClientCallsEnabled()) + return; + + page->setMemoryCacheClientCallsEnabled(false); + + postResolutionCallbackQueue().append([protectedMainFrame = Ref<MainFrame>(page->mainFrame())] { + if (Page* page = protectedMainFrame->page()) + page->setMemoryCacheClientCallsEnabled(true); + }); +} + +static unsigned resolutionNestingDepth; + +PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document) +{ + ++resolutionNestingDepth; + + if (resolutionNestingDepth == 1) + platformStrategies()->loaderStrategy()->suspendPendingRequests(); + + // FIXME: It's strange to build this into the disabler. + suspendMemoryCacheClientCalls(document); +} + +PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler() +{ + if (resolutionNestingDepth == 1) { + // Get size each time through the loop because a callback can add more callbacks to the end of the queue. + auto& queue = postResolutionCallbackQueue(); + for (size_t i = 0; i < queue.size(); ++i) + queue[i](); + queue.clear(); + + platformStrategies()->loaderStrategy()->resumePendingRequests(); + } + + --resolutionNestingDepth; +} + +bool postResolutionCallbacksAreSuspended() +{ + return resolutionNestingDepth; +} + +} +} diff --git a/Source/WebCore/style/StyleTreeResolver.h b/Source/WebCore/style/StyleTreeResolver.h new file mode 100644 index 000000000..df5f861f3 --- /dev/null +++ b/Source/WebCore/style/StyleTreeResolver.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "RenderStyleConstants.h" +#include "RenderTreePosition.h" +#include "SelectorChecker.h" +#include "SelectorFilter.h" +#include "StyleChange.h" +#include "StyleSharingResolver.h" +#include "StyleUpdate.h" +#include "StyleValidity.h" +#include <wtf/Function.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Document; +class Element; +class Node; +class RenderStyle; +class ShadowRoot; +class StyleResolver; + +namespace Style { + +class TreeResolver { +public: + TreeResolver(Document&); + ~TreeResolver(); + + std::unique_ptr<Update> resolve(Change); + + static ElementUpdate createAnimatedElementUpdate(std::unique_ptr<RenderStyle>, Element&, Change parentChange); + +private: + std::unique_ptr<RenderStyle> styleForElement(Element&, const RenderStyle& inheritedStyle); + + void resolveComposedTree(); + ElementUpdate resolveElement(Element&); + + struct Scope : RefCounted<Scope> { + StyleResolver& styleResolver; + SelectorFilter selectorFilter; + SharingResolver sharingResolver; + ShadowRoot* shadowRoot { nullptr }; + Scope* enclosingScope { nullptr }; + + Scope(Document&); + Scope(ShadowRoot&, Scope& enclosingScope); + }; + + struct Parent { + Element* element; + const RenderStyle& style; + Change change; + bool didPushScope { false }; + bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle { false }; + + Parent(Document&, Change); + Parent(Element&, const RenderStyle&, Change); + }; + + Scope& scope() { return m_scopeStack.last(); } + Parent& parent() { return m_parentStack.last(); } + + void pushScope(ShadowRoot&); + void pushEnclosingScope(); + void popScope(); + + void pushParent(Element&, const RenderStyle&, Change); + void popParent(); + void popParentsToDepth(unsigned depth); + + const RenderStyle* parentBoxStyle() const; + + Document& m_document; + std::unique_ptr<RenderStyle> m_documentElementStyle; + + Vector<Ref<Scope>, 4> m_scopeStack; + Vector<Parent, 32> m_parentStack; + + std::unique_ptr<Update> m_update; +}; + +void queuePostResolutionCallback(Function<void ()>&&); +bool postResolutionCallbacksAreSuspended(); + +class PostResolutionCallbackDisabler { +public: + explicit PostResolutionCallbackDisabler(Document&); + ~PostResolutionCallbackDisabler(); +}; + +} + +} diff --git a/Source/WebCore/style/StyleUpdate.cpp b/Source/WebCore/style/StyleUpdate.cpp new file mode 100644 index 000000000..9b2cecb87 --- /dev/null +++ b/Source/WebCore/style/StyleUpdate.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StyleUpdate.h" + +#include "ComposedTreeAncestorIterator.h" +#include "Document.h" +#include "Element.h" +#include "NodeRenderStyle.h" +#include "RenderElement.h" +#include "Text.h" + +namespace WebCore { +namespace Style { + +Update::Update(Document& document) + : m_document(document) +{ +} + +const ElementUpdate* Update::elementUpdate(const Element& element) const +{ + auto it = m_elements.find(&element); + if (it == m_elements.end()) + return nullptr; + return &it->value; +} + +ElementUpdate* Update::elementUpdate(const Element& element) +{ + auto it = m_elements.find(&element); + if (it == m_elements.end()) + return nullptr; + return &it->value; +} + +bool Update::textUpdate(const Text& text) const +{ + return m_texts.contains(&text); +} + +const RenderStyle* Update::elementStyle(const Element& element) const +{ + if (auto* update = elementUpdate(element)) + return update->style.get(); + auto* renderer = element.renderer(); + if (!renderer) + return nullptr; + return &renderer->style(); +} + +RenderStyle* Update::elementStyle(const Element& element) +{ + if (auto* update = elementUpdate(element)) + return update->style.get(); + auto* renderer = element.renderer(); + if (!renderer) + return nullptr; + return &renderer->mutableStyle(); +} + +void Update::addElement(Element& element, Element* parent, ElementUpdate&& elementUpdate) +{ + ASSERT(!m_elements.contains(&element)); + ASSERT(composedTreeAncestors(element).first() == parent); + + addPossibleRoot(parent); + m_elements.add(&element, WTFMove(elementUpdate)); +} + +void Update::addText(Text& text, Element* parent) +{ + ASSERT(!m_texts.contains(&text)); + ASSERT(composedTreeAncestors(text).first() == parent); + + addPossibleRoot(parent); + m_texts.add(&text); +} + +void Update::addText(Text& text) +{ + addText(text, composedTreeAncestors(text).first()); +} + +void Update::addPossibleRoot(Element* element) +{ + if (!element) { + m_roots.add(&m_document); + return; + } + if (m_elements.contains(element)) + return; + m_roots.add(element); +} + +} +} diff --git a/Source/WebCore/style/StyleUpdate.h b/Source/WebCore/style/StyleUpdate.h new file mode 100644 index 000000000..176126948 --- /dev/null +++ b/Source/WebCore/style/StyleUpdate.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Node.h" +#include "StyleChange.h" +#include "StyleRelations.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/ListHashSet.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class ContainerNode; +class Document; +class Element; +class Node; +class RenderStyle; +class Text; + +namespace Style { + +struct ElementUpdate { + ElementUpdate() = default; + ElementUpdate(std::unique_ptr<RenderStyle> style, Change change, bool recompositeLayer) + : style(WTFMove(style)) + , change(change) + , recompositeLayer(recompositeLayer) + { } + std::unique_ptr<RenderStyle> style; + Change change { NoChange }; + bool recompositeLayer { false }; +}; + +class Update { + WTF_MAKE_FAST_ALLOCATED; +public: + Update(Document&); + + const ListHashSet<ContainerNode*>& roots() const { return m_roots; } + + const ElementUpdate* elementUpdate(const Element&) const; + ElementUpdate* elementUpdate(const Element&); + + bool textUpdate(const Text&) const; + + const RenderStyle* elementStyle(const Element&) const; + RenderStyle* elementStyle(const Element&); + + const Document& document() const { return m_document; } + + unsigned size() const { return m_elements.size() + m_texts.size(); } + + void addElement(Element&, Element* parent, ElementUpdate&&); + void addText(Text&, Element* parent); + void addText(Text&); + +private: + void addPossibleRoot(Element*); + + Document& m_document; + ListHashSet<ContainerNode*> m_roots; + HashMap<const Element*, ElementUpdate> m_elements; + HashSet<const Text*> m_texts; +}; + +} +} diff --git a/Source/WebCore/style/StyleValidity.h b/Source/WebCore/style/StyleValidity.h new file mode 100644 index 000000000..97fe6be1b --- /dev/null +++ b/Source/WebCore/style/StyleValidity.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { +namespace Style { + +enum class Validity { + Valid, + ElementInvalid, + SubtreeInvalid, + SubtreeAndRenderersInvalid +}; + +enum class InvalidationMode { + Normal, + RecompositeLayer +}; + +} +} |