summaryrefslogtreecommitdiff
path: root/Source/WebCore/style
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/style
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/style')
-rw-r--r--Source/WebCore/style/AttributeChangeInvalidation.cpp124
-rw-r--r--Source/WebCore/style/AttributeChangeInvalidation.h69
-rw-r--r--Source/WebCore/style/ClassChangeInvalidation.cpp158
-rw-r--r--Source/WebCore/style/ClassChangeInvalidation.h73
-rw-r--r--Source/WebCore/style/IdChangeInvalidation.cpp92
-rw-r--r--Source/WebCore/style/IdChangeInvalidation.h68
-rw-r--r--Source/WebCore/style/InlineTextBoxStyle.cpp166
-rw-r--r--Source/WebCore/style/InlineTextBoxStyle.h51
-rw-r--r--Source/WebCore/style/RenderTreePosition.cpp161
-rw-r--r--Source/WebCore/style/RenderTreePosition.h101
-rw-r--r--Source/WebCore/style/RenderTreeUpdater.cpp636
-rw-r--r--Source/WebCore/style/RenderTreeUpdater.h86
-rw-r--r--Source/WebCore/style/StyleChange.cpp88
-rw-r--r--Source/WebCore/style/StyleChange.h (renamed from Source/WebCore/style/StyleResolveTree.h)23
-rw-r--r--Source/WebCore/style/StyleFontSizeFunctions.cpp37
-rw-r--r--Source/WebCore/style/StyleFontSizeFunctions.h10
-rw-r--r--Source/WebCore/style/StylePendingResources.cpp95
-rw-r--r--Source/WebCore/style/StylePendingResources.h39
-rw-r--r--Source/WebCore/style/StyleRelations.cpp157
-rw-r--r--Source/WebCore/style/StyleRelations.h75
-rw-r--r--Source/WebCore/style/StyleResolveForDocument.cpp89
-rw-r--r--Source/WebCore/style/StyleResolveForDocument.h9
-rw-r--r--Source/WebCore/style/StyleResolveTree.cpp915
-rw-r--r--Source/WebCore/style/StyleScope.cpp568
-rw-r--r--Source/WebCore/style/StyleScope.h181
-rw-r--r--Source/WebCore/style/StyleSharingResolver.cpp351
-rw-r--r--Source/WebCore/style/StyleSharingResolver.h70
-rw-r--r--Source/WebCore/style/StyleTreeResolver.cpp560
-rw-r--r--Source/WebCore/style/StyleTreeResolver.h120
-rw-r--r--Source/WebCore/style/StyleUpdate.cpp120
-rw-r--r--Source/WebCore/style/StyleUpdate.h92
-rw-r--r--Source/WebCore/style/StyleValidity.h44
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, &current.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(&current); 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(&current);
-
- // 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(&current);
-
- 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() == &current && 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(&current);
- bool hasParentStyle = renderingParentNode && renderingParentNode->renderStyle();
- bool hasDirectAdjacentRules = current.childrenAffectedByDirectAdjacentRules();
- bool hasIndirectAdjacentRules = current.childrenAffectedByForwardPositionalRules();
-
-#if PLATFORM(IOS)
- CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(&current, 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(&current);
-
- 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
+};
+
+}
+}